Compare commits

..

1 Commits
1.26.1 ... midi

Author SHA1 Message Date
WarmUpTill
c7afdd817b Add MIDI device name check 2024-05-10 01:15:16 +02:00
30 changed files with 606 additions and 806 deletions

View File

@@ -128,8 +128,6 @@ target_sources(
lib/macro/macro-ref.hpp lib/macro/macro-ref.hpp
lib/macro/macro-run-button.cpp lib/macro/macro-run-button.cpp
lib/macro/macro-run-button.hpp 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.cpp
lib/macro/macro-segment-list.hpp lib/macro/macro-segment-list.hpp
lib/macro/macro-segment-selection.cpp lib/macro/macro-segment-selection.cpp

View File

@@ -469,6 +469,7 @@ AdvSceneSwitcher.action.macro.type.unpause="Nicht mehr pausieren"
AdvSceneSwitcher.action.macro.type.resetCounter="Zähler zurücksetzen" AdvSceneSwitcher.action.macro.type.resetCounter="Zähler zurücksetzen"
AdvSceneSwitcher.action.macro.type.run="Aktionen ausführen" AdvSceneSwitcher.action.macro.type.run="Aktionen ausführen"
AdvSceneSwitcher.action.macro.type.stop="Aktionen stoppen" AdvSceneSwitcher.action.macro.type.stop="Aktionen stoppen"
AdvSceneSwitcher.action.macro.entry="{{actions}}{{actionIndex}}{{macros}}"
AdvSceneSwitcher.action.pluginState="Plugin-Status" AdvSceneSwitcher.action.pluginState="Plugin-Status"
AdvSceneSwitcher.action.pluginState.type.stop="Erweiterten Automatischen Szenenwechsler stoppen" AdvSceneSwitcher.action.pluginState.type.stop="Erweiterten Automatischen Szenenwechsler stoppen"
AdvSceneSwitcher.action.pluginState.type.noMatch="Ändern des Nichtübereinstimmungsverhaltens:" AdvSceneSwitcher.action.pluginState.type.noMatch="Ändern des Nichtübereinstimmungsverhaltens:"

View File

@@ -177,8 +177,6 @@ AdvSceneSwitcher.macroTab.minimize="Minimize"
AdvSceneSwitcher.macroTab.segment.useCustomLabel="Use custom label" AdvSceneSwitcher.macroTab.segment.useCustomLabel="Use custom label"
AdvSceneSwitcher.macroTab.segment.defaultCustomLabel="My label" AdvSceneSwitcher.macroTab.segment.defaultCustomLabel="My label"
AdvSceneSwitcher.macroTab.segment.setCustomLabel="Set label ..." AdvSceneSwitcher.macroTab.segment.setCustomLabel="Set label ..."
AdvSceneSwitcher.macroTab.segment.copy="Copy"
AdvSceneSwitcher.macroTab.segment.paste="Paste"
AdvSceneSwitcher.macroTab.highlightSettings="Visual settings" AdvSceneSwitcher.macroTab.highlightSettings="Visual settings"
AdvSceneSwitcher.macroTab.hotkeySettings="Hotkey settings" AdvSceneSwitcher.macroTab.hotkeySettings="Hotkey settings"
AdvSceneSwitcher.macroTab.generalSettings="General settings" AdvSceneSwitcher.macroTab.generalSettings="General settings"
@@ -594,7 +592,15 @@ AdvSceneSwitcher.condition.run="Run"
AdvSceneSwitcher.condition.run.entry="Process exits before timeout of{{timeout}}seconds" AdvSceneSwitcher.condition.run.entry="Process exits before timeout of{{timeout}}seconds"
AdvSceneSwitcher.condition.run.entry.exit="{{checkExitCode}}Check for exit code{{exitCode}}" AdvSceneSwitcher.condition.run.entry.exit="{{checkExitCode}}Check for exit code{{exitCode}}"
AdvSceneSwitcher.condition.midi="MIDI" AdvSceneSwitcher.condition.midi="MIDI"
<<<<<<< HEAD
AdvSceneSwitcher.condition.midi.entry="Message was received from{{device}}which matches:" AdvSceneSwitcher.condition.midi.entry="Message was received from{{device}}which matches:"
=======
AdvSceneSwitcher.condition.midi.condition.message="MIDI message was received"
AdvSceneSwitcher.condition.midi.condition.deviceName="MIDI device is connected"
AdvSceneSwitcher.condition.midi.condition.deviceName.info="On some platforms with certain APIs dynamic MIDI device detection might not function.\nSo this type of check might not be 100% reliable in all circumstances."
AdvSceneSwitcher.condition.midi.entry.message="{{conditions}}from{{device}}which matches:"
AdvSceneSwitcher.condition.midi.entry.deviceName="{{conditions}}{{deviceNames}}{{regex}}{{deviceListInfo}}"
>>>>>>> c4e35d1a (Add MIDI device name check)
AdvSceneSwitcher.condition.midi.entry.listen="Set MIDI message selection to messages incoming on selected device:{{listenButton}}" AdvSceneSwitcher.condition.midi.entry.listen="Set MIDI message selection to messages incoming on selected device:{{listenButton}}"
AdvSceneSwitcher.condition.display="Display" AdvSceneSwitcher.condition.display="Display"
AdvSceneSwitcher.condition.display.type.displayName="Name of connected displays matches" AdvSceneSwitcher.condition.display.type.displayName="Name of connected displays matches"
@@ -814,20 +820,12 @@ AdvSceneSwitcher.action.macro="Macro"
AdvSceneSwitcher.action.macro.type.pause="Pause" AdvSceneSwitcher.action.macro.type.pause="Pause"
AdvSceneSwitcher.action.macro.type.unpause="Unpause" AdvSceneSwitcher.action.macro.type.unpause="Unpause"
AdvSceneSwitcher.action.macro.type.resetCounter="Reset counter" AdvSceneSwitcher.action.macro.type.resetCounter="Reset counter"
AdvSceneSwitcher.action.macro.type.run="Run macro" AdvSceneSwitcher.action.macro.type.run="Run actions"
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.stop="Stop actions"
AdvSceneSwitcher.action.macro.type.disableAction="Disable action" AdvSceneSwitcher.action.macro.type.disableAction="Disable action"
AdvSceneSwitcher.action.macro.type.enableAction="Enable action" AdvSceneSwitcher.action.macro.type.enableAction="Enable action"
AdvSceneSwitcher.action.macro.type.toggleAction="Toggle action" AdvSceneSwitcher.action.macro.type.toggleAction="Toggle action"
AdvSceneSwitcher.action.macro.entry.run="{{actions}}{{actionTypes}}of{{macros}}" AdvSceneSwitcher.action.macro.entry="{{actions}}{{actionIndex}}{{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="Plugin state"
AdvSceneSwitcher.action.pluginState.type.stop="Stop the Advanced Scene Switcher plugin" AdvSceneSwitcher.action.pluginState.type.stop="Stop the Advanced Scene Switcher plugin"
AdvSceneSwitcher.action.pluginState.type.noMatch="Change the no-match behaviour:" AdvSceneSwitcher.action.pluginState.type.noMatch="Change the no-match behaviour:"

View File

@@ -386,6 +386,7 @@ AdvSceneSwitcher.action.macro.type.unpause="Reanudar"
AdvSceneSwitcher.action.macro.type.resetCounter="Reiniciar contador" AdvSceneSwitcher.action.macro.type.resetCounter="Reiniciar contador"
AdvSceneSwitcher.action.macro.type.run="Ejecutar" AdvSceneSwitcher.action.macro.type.run="Ejecutar"
AdvSceneSwitcher.action.macro.type.stop="Detener" AdvSceneSwitcher.action.macro.type.stop="Detener"
AdvSceneSwitcher.action.macro.entry="{{actions}}{{actionIndex}}{{macros}}"
AdvSceneSwitcher.action.pluginState="Estado del complemento" AdvSceneSwitcher.action.pluginState="Estado del complemento"
AdvSceneSwitcher.action.pluginState.type.stop="Detener el complemento Advanced Scene Switcher" AdvSceneSwitcher.action.pluginState.type.stop="Detener el complemento Advanced Scene Switcher"
AdvSceneSwitcher.action.pluginState.type.noMatch="Cambiar el comportamiento de no coincidencia:" AdvSceneSwitcher.action.pluginState.type.noMatch="Cambiar el comportamiento de no coincidencia:"

View File

@@ -446,7 +446,6 @@ AdvSceneSwitcher.condition.run="Exécution"
AdvSceneSwitcher.condition.run.entry="Le processus se termine avant le délai de{{timeout}}secondes" AdvSceneSwitcher.condition.run.entry="Le processus se termine avant le délai de{{timeout}}secondes"
AdvSceneSwitcher.condition.run.entry.exit="{{checkExitCode}}Vérifier le code de sortie{{exitCode}} AdvSceneSwitcher.condition.run.entry.exit="{{checkExitCode}}Vérifier le code de sortie{{exitCode}}
AdvSceneSwitcher.condition.midi="MIDI" AdvSceneSwitcher.condition.midi="MIDI"
AdvSceneSwitcher.condition.midi.entry="Message reçu depuis{{device}}qui correspond à :"
AdvSceneSwitcher.condition.midi.entry.listen="Définir la sélection des messages MIDI pour les messages entrants sur l'appareil sélectionné :{{listenButton}}" AdvSceneSwitcher.condition.midi.entry.listen="Définir la sélection des messages MIDI pour les messages entrants sur l'appareil sélectionné :{{listenButton}}"
AdvSceneSwitcher.condition.display="Affichage" AdvSceneSwitcher.condition.display="Affichage"
AdvSceneSwitcher.condition.display.type.displayName="Le nom des écrans connectés correspond à" AdvSceneSwitcher.condition.display.type.displayName="Le nom des écrans connectés correspond à"

View File

@@ -315,6 +315,7 @@ AdvSceneSwitcher.action.macro.type.pause="Duraklat"
AdvSceneSwitcher.action.macro.type.unpause="Duraklatma" AdvSceneSwitcher.action.macro.type.unpause="Duraklatma"
AdvSceneSwitcher.action.macro.type.resetCounter="Sayacı sıfırla" AdvSceneSwitcher.action.macro.type.resetCounter="Sayacı sıfırla"
AdvSceneSwitcher.action.macro.type.run="Çalıştır" AdvSceneSwitcher.action.macro.type.run="Çalıştır"
AdvSceneSwitcher.action.macro.entry="{{actions}}{{actionIndex}}{{macros}}"
AdvSceneSwitcher.action.pluginState="Eklenti durumu" AdvSceneSwitcher.action.pluginState="Eklenti durumu"
AdvSceneSwitcher.action.pluginState.type.stop="Advanced Scene Switcher eklentisini durdurun" AdvSceneSwitcher.action.pluginState.type.stop="Advanced Scene Switcher eklentisini durdurun"
AdvSceneSwitcher.action.pluginState.type.noMatch="Eşleşmeme davranışını değiştirin:" AdvSceneSwitcher.action.pluginState.type.noMatch="Eşleşmeme davranışını değiştirin:"

View File

@@ -434,7 +434,6 @@ AdvSceneSwitcher.condition.run="运行"
AdvSceneSwitcher.condition.run.entry="进程在超时 {{timeout}} 秒之前退出" AdvSceneSwitcher.condition.run.entry="进程在超时 {{timeout}} 秒之前退出"
AdvSceneSwitcher.condition.run.entry.exit="{{checkExitCode}}检查退出代码{{exitCode}}" AdvSceneSwitcher.condition.run.entry.exit="{{checkExitCode}}检查退出代码{{exitCode}}"
AdvSceneSwitcher.condition.midi="MIDI" AdvSceneSwitcher.condition.midi="MIDI"
AdvSceneSwitcher.condition.midi.entry="从{{device}} 收到的信息,符合:(从不咕咕的阿坤:这个好像是声卡工作是录音相关的功能,英文太抽象了,我就硬译过来了,如有相关设备,音乐好的,可以根据英文翻译好发给我!)"
AdvSceneSwitcher.condition.midi.entry.listen="将MIDI信息选择设置为从选定设备上接收的信息: {{listenButton}}" AdvSceneSwitcher.condition.midi.entry.listen="将MIDI信息选择设置为从选定设备上接收的信息: {{listenButton}}"
AdvSceneSwitcher.condition.display="显示器" AdvSceneSwitcher.condition.display="显示器"
AdvSceneSwitcher.condition.display.type.displayName="连接的显示器的名称匹配" AdvSceneSwitcher.condition.display.type.displayName="连接的显示器的名称匹配"
@@ -524,6 +523,7 @@ AdvSceneSwitcher.action.macro.type.unpause="取消暂停"
AdvSceneSwitcher.action.macro.type.resetCounter="复位计数器" AdvSceneSwitcher.action.macro.type.resetCounter="复位计数器"
AdvSceneSwitcher.action.macro.type.run="运行操作" AdvSceneSwitcher.action.macro.type.run="运行操作"
AdvSceneSwitcher.action.macro.type.stop="停止操作" AdvSceneSwitcher.action.macro.type.stop="停止操作"
AdvSceneSwitcher.action.macro.entry="{{actions}}{{actionIndex}}{{macros}}"
AdvSceneSwitcher.action.pluginState="插件状态" AdvSceneSwitcher.action.pluginState="插件状态"
AdvSceneSwitcher.action.pluginState.type.stop="停止高级场景切换插件" AdvSceneSwitcher.action.pluginState.type.stop="停止高级场景切换插件"
AdvSceneSwitcher.action.pluginState.type.noMatch="没有匹配项时:" AdvSceneSwitcher.action.pluginState.type.noMatch="没有匹配项时:"

View File

@@ -240,7 +240,7 @@ void SwitcherData::Thread()
if (duration.count() < 1) { if (duration.count() < 1) {
blog(LOG_INFO, blog(LOG_INFO,
"detected busy loop - refusing to sleep less than 1ms"); "detected busy loop - refusing to sleep less than 1ms");
duration = std::chrono::milliseconds(10); duration = std::chrono::milliseconds(50);
} }
} }

View File

@@ -12,7 +12,6 @@ class MacroActionEdit;
class MacroConditionEdit; class MacroConditionEdit;
class Duration; class Duration;
class SequenceWidget; class SequenceWidget;
enum class LogicType;
struct SceneGroup; struct SceneGroup;
/******************************************************************************* /*******************************************************************************
@@ -153,16 +152,12 @@ public slots:
void SetElseActionsStateToVisible(); void SetElseActionsStateToVisible();
void MacroActionSelectionChanged(int idx); void MacroActionSelectionChanged(int idx);
void MacroActionReorder(int to, int target); 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 AddMacroAction(int idx);
void RemoveMacroAction(int idx); void RemoveMacroAction(int idx);
void MoveMacroActionUp(int idx); void MoveMacroActionUp(int idx);
void MoveMacroActionDown(int idx); void MoveMacroActionDown(int idx);
void MacroElseActionSelectionChanged(int idx); void MacroElseActionSelectionChanged(int idx);
void MacroElseActionReorder(int to, int target); 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 AddMacroElseAction(int idx);
void RemoveMacroElseAction(int idx); void RemoveMacroElseAction(int idx);
void SwapElseActions(Macro *m, int pos1, int pos2); void SwapElseActions(Macro *m, int pos1, int pos2);
@@ -171,16 +166,16 @@ public slots:
void MacroConditionSelectionChanged(int idx); void MacroConditionSelectionChanged(int idx);
void MacroConditionReorder(int to, int target); void MacroConditionReorder(int to, int target);
void AddMacroCondition(int idx); 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 RemoveMacroCondition(int idx);
void MoveMacroConditionUp(int idx); void MoveMacroConditionUp(int idx);
void MoveMacroConditionDown(int idx); void MoveMacroConditionDown(int idx);
void FadeOutActionControls();
void FadeOutConditionControls();
void ResetOpacityActionControls();
void ResetOpacityConditionControls();
void HighlightControls(); void HighlightControls();
void HighlightOnChange(); void HighlightOnChange();
void on_macroProperties_clicked(); void on_macroProperties_clicked();
void CopyMacroSegment();
void PasteMacroSegment();
signals: signals:
void MacroAdded(const QString &name); void MacroAdded(const QString &name);
@@ -189,6 +184,9 @@ signals:
void MacroSegmentOrderChanged(); void MacroSegmentOrderChanged();
void SegmentTempVarsChanged(); void SegmentTempVarsChanged();
void HighlightMacrosChanged(bool value); void HighlightMacrosChanged(bool value);
void HighlightActionsChanged(bool value);
void HighlightElseActionsChanged(bool value);
void HighlightConditionsChanged(bool value);
void ConnectionAdded(const QString &); void ConnectionAdded(const QString &);
void ConnectionRenamed(const QString &oldName, const QString &newName); void ConnectionRenamed(const QString &oldName, const QString &newName);

View File

@@ -29,7 +29,8 @@ static inline void populateActionSelection(QComboBox *list)
MacroActionEdit::MacroActionEdit(QWidget *parent, MacroActionEdit::MacroActionEdit(QWidget *parent,
std::shared_ptr<MacroAction> *entryData, std::shared_ptr<MacroAction> *entryData,
const std::string &id) const std::string &id)
: MacroSegmentEdit(parent), : MacroSegmentEdit(GetGlobalMacroProperties()._highlightActions,
parent),
_actionSelection(new FilterComboBox()), _actionSelection(new FilterComboBox()),
_enable(new SwitchButton()), _enable(new SwitchButton()),
_entryData(entryData) _entryData(entryData)
@@ -39,6 +40,8 @@ MacroActionEdit::MacroActionEdit(QWidget *parent,
SLOT(ActionSelectionChanged(const QString &))); SLOT(ActionSelectionChanged(const QString &)));
QWidget::connect(_enable, SIGNAL(checked(bool)), this, QWidget::connect(_enable, SIGNAL(checked(bool)), this,
SLOT(ActionEnableChanged(bool))); SLOT(ActionEnableChanged(bool)));
QWidget::connect(window(), SIGNAL(HighlightActionsChanged(bool)), this,
SLOT(EnableHighlight(bool)));
QWidget::connect(&_actionStateTimer, SIGNAL(timeout()), this, QWidget::connect(&_actionStateTimer, SIGNAL(timeout()), this,
SLOT(UpdateActionState())); SLOT(UpdateActionState()));
@@ -158,20 +161,33 @@ std::shared_ptr<MacroSegment> MacroActionEdit::Data() const
return *_entryData; return *_entryData;
} }
void AdvSceneSwitcher::AddMacroAction(Macro *macro, int idx, void AdvSceneSwitcher::AddMacroAction(int idx)
const std::string &id, obs_data_t *data)
{ {
if (idx < 0 || idx > (int)macro->Actions().size()) { auto macro = GetSelectedMacro();
assert(false); if (!macro) {
return; return;
} }
if (idx < 0 || idx > (int)macro->Actions().size()) {
return;
}
std::string id;
if (idx - 1 >= 0) {
id = macro->Actions().at(idx - 1)->GetId();
} else {
id = MacroAction::GetDefaultID();
}
{ {
auto lock = LockContext(); auto lock = LockContext();
macro->Actions().emplace(macro->Actions().begin() + idx, macro->Actions().emplace(
MacroActionFactory::Create(id, macro)); macro->Actions().begin() + idx,
if (data) { MacroActionFactory::Create(id, macro.get()));
if (idx - 1 >= 0) {
auto data = obs_data_create();
macro->Actions().at(idx - 1)->Save(data);
macro->Actions().at(idx)->Load(data); macro->Actions().at(idx)->Load(data);
obs_data_release(data);
} }
macro->Actions().at(idx)->PostLoad(); macro->Actions().at(idx)->PostLoad();
RunPostLoadSteps(); RunPostLoadSteps();
@@ -182,37 +198,9 @@ void AdvSceneSwitcher::AddMacroAction(Macro *macro, int idx,
SetActionData(*macro); SetActionData(*macro);
} }
HighlightAction(idx); HighlightAction(idx);
ui->actionsList->SetHelpMsgVisible(false);
emit(MacroSegmentOrderChanged()); 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() void AdvSceneSwitcher::on_actionAdd_clicked()
{ {
auto macro = GetSelectedMacro(); auto macro = GetSelectedMacro();
@@ -228,6 +216,7 @@ void AdvSceneSwitcher::on_actionAdd_clicked()
if (currentActionIdx != -1) { if (currentActionIdx != -1) {
MacroActionSelectionChanged(currentActionIdx + 1); MacroActionSelectionChanged(currentActionIdx + 1);
} }
ui->actionsList->SetHelpMsgVisible(false);
} }
void AdvSceneSwitcher::RemoveMacroAction(int idx) void AdvSceneSwitcher::RemoveMacroAction(int idx)
@@ -322,6 +311,7 @@ void AdvSceneSwitcher::on_elseActionAdd_clicked()
if (currentElseActionIdx != -1) { if (currentElseActionIdx != -1) {
MacroElseActionSelectionChanged(currentElseActionIdx + 1); MacroElseActionSelectionChanged(currentElseActionIdx + 1);
} }
ui->elseActionsList->SetHelpMsgVisible(false);
} }
void AdvSceneSwitcher::on_elseActionRemove_clicked() void AdvSceneSwitcher::on_elseActionRemove_clicked()
@@ -460,21 +450,31 @@ void AdvSceneSwitcher::MacroElseActionReorder(int to, int from)
emit(MacroSegmentOrderChanged()); emit(MacroSegmentOrderChanged());
} }
void AdvSceneSwitcher::AddMacroElseAction(Macro *macro, int idx, void AdvSceneSwitcher::AddMacroElseAction(int idx)
const std::string &id,
obs_data_t *data)
{ {
if (idx < 0 || idx > (int)macro->ElseActions().size()) { auto macro = GetSelectedMacro();
assert(false); if (!macro) {
return; return;
} }
if (idx < 0 || idx > (int)macro->ElseActions().size()) {
return;
}
std::string id;
if (idx - 1 >= 0) {
id = macro->ElseActions().at(idx - 1)->GetId();
} else {
id = MacroAction::GetDefaultID();
}
{ {
auto lock = LockContext(); auto lock = LockContext();
macro->ElseActions().emplace(macro->ElseActions().begin() + idx, macro->ElseActions().emplace(
MacroActionFactory::Create(id, macro->ElseActions().begin() + idx,
macro)); MacroActionFactory::Create(id, macro.get()));
if (data) { if (idx - 1 >= 0) {
OBSDataAutoRelease data = obs_data_create();
macro->ElseActions().at(idx - 1)->Save(data);
macro->ElseActions().at(idx)->Load(data); macro->ElseActions().at(idx)->Load(data);
} }
macro->ElseActions().at(idx)->PostLoad(); macro->ElseActions().at(idx)->PostLoad();
@@ -486,37 +486,9 @@ void AdvSceneSwitcher::AddMacroElseAction(Macro *macro, int idx,
SetElseActionData(*macro); SetElseActionData(*macro);
} }
HighlightElseAction(idx); HighlightElseAction(idx);
ui->elseActionsList->SetHelpMsgVisible(false);
emit(MacroSegmentOrderChanged()); 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) void AdvSceneSwitcher::RemoveMacroElseAction(int idx)
{ {
auto macro = GetSelectedMacro(); auto macro = GetSelectedMacro();
@@ -535,7 +507,7 @@ void AdvSceneSwitcher::RemoveMacroElseAction(int idx)
SetMacroAbortWait(true); SetMacroAbortWait(true);
GetMacroWaitCV().notify_all(); GetMacroWaitCV().notify_all();
macro->UpdateElseActionIndices(); macro->UpdateElseActionIndices();
SetElseActionData(*macro); SetActionData(*macro);
} }
MacroElseActionSelectionChanged(-1); MacroElseActionSelectionChanged(-1);
lastInteracted = MacroSection::ELSE_ACTIONS; lastInteracted = MacroSection::ELSE_ACTIONS;

View File

@@ -48,7 +48,9 @@ bool MacroActionMacro::PerformAction()
macro->ResetRunCount(); macro->ResetRunCount();
break; break;
case Action::RUN: case Action::RUN:
RunActions(macro.get()); if (!macro->Paused()) {
macro->PerformActions(true, false, true);
}
break; break;
case Action::STOP: case Action::STOP:
macro->Stop(); macro->Stop();
@@ -125,7 +127,6 @@ bool MacroActionMacro::Save(obs_data_t *obj) const
_macro.Save(obj); _macro.Save(obj);
_actionIndex.Save(obj, "actionIndex"); _actionIndex.Save(obj, "actionIndex");
obs_data_set_int(obj, "action", static_cast<int>(_action)); obs_data_set_int(obj, "action", static_cast<int>(_action));
_runOptions.Save(obj);
return true; return true;
} }
@@ -136,15 +137,6 @@ bool MacroActionMacro::Load(obs_data_t *obj)
_actionIndex.Load(obj, "actionIndex"); _actionIndex.Load(obj, "actionIndex");
_action = static_cast<MacroActionMacro::Action>( _action = static_cast<MacroActionMacro::Action>(
obs_data_get_int(obj, "action")); obs_data_get_int(obj, "action"));
_runOptions.Load(obj);
return true;
}
bool MacroActionMacro::PostLoad()
{
MacroRefAction::PostLoad();
MacroAction::PostLoad();
_runOptions.macro.PostLoad();
return true; return true;
} }
@@ -168,55 +160,11 @@ void MacroActionMacro::ResolveVariablesToFixedValues()
_actionIndex.ResolveVariables(); _actionIndex.ResolveVariables();
} }
void MacroActionMacro::RunActions(Macro *actionMacro) const static inline void populateActionSelection(QComboBox *list)
{ {
if (_runOptions.skipWhenPaused && actionMacro->Paused()) { for (auto entry : actionTypes) {
return; list->addItem(obs_module_text(entry.second.c_str()));
} }
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( MacroActionMacroEdit::MacroActionMacroEdit(
@@ -225,20 +173,9 @@ MacroActionMacroEdit::MacroActionMacroEdit(
_macros(new MacroSelection(parent)), _macros(new MacroSelection(parent)),
_actionIndex(new MacroSegmentSelection( _actionIndex(new MacroSegmentSelection(
this, MacroSegmentSelection::Type::ACTION)), 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); populateActionSelection(_actions);
populateConditionBehaviorSelection(_conditionBehaviors);
populateActionTypeSelection(_actionTypes);
_conditionMacros->HideSelectedMacro();
QWidget::connect(_macros, SIGNAL(currentTextChanged(const QString &)), QWidget::connect(_macros, SIGNAL(currentTextChanged(const QString &)),
this, SLOT(MacroChanged(const QString &))); this, SLOT(MacroChanged(const QString &)));
@@ -247,21 +184,15 @@ MacroActionMacroEdit::MacroActionMacroEdit(
QWidget::connect(_actionIndex, QWidget::connect(_actionIndex,
SIGNAL(SelectionChanged(const IntVariable &)), this, SIGNAL(SelectionChanged(const IntVariable &)), this,
SLOT(ActionIndexChanged(const IntVariable &))); 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 layout = new QVBoxLayout(); auto mainLayout = new QHBoxLayout;
layout->addLayout(_entryLayout); PlaceWidgets(obs_module_text("AdvSceneSwitcher.action.macro.entry"),
layout->addLayout(_conditionLayout); mainLayout,
layout->addWidget(_skipWhenPaused); {{"{{actions}}", _actions},
setLayout(layout); {"{{actionIndex}}", _actionIndex},
{"{{macros}}", _macros}});
setLayout(mainLayout);
_entryData = entryData; _entryData = entryData;
UpdateEntryData(); UpdateEntryData();
_loading = false; _loading = false;
@@ -276,12 +207,6 @@ void MacroActionMacroEdit::UpdateEntryData()
_actionIndex->SetValue(_entryData->_actionIndex); _actionIndex->SetValue(_entryData->_actionIndex);
_actionIndex->SetMacro(_entryData->_macro.GetMacro()); _actionIndex->SetMacro(_entryData->_macro.GetMacro());
_macros->SetCurrentMacro(_entryData->_macro); _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(); SetWidgetVisibility();
} }
@@ -319,88 +244,8 @@ void MacroActionMacroEdit::ActionIndexChanged(const IntVariable &value)
_entryData->_actionIndex = 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() 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 || if (_entryData->_action == MacroActionMacro::Action::RUN ||
_entryData->_action == MacroActionMacro::Action::STOP) { _entryData->_action == MacroActionMacro::Action::STOP) {
_macros->HideSelectedMacro(); _macros->HideSelectedMacro();
@@ -415,42 +260,6 @@ void MacroActionMacroEdit::SetWidgetVisibility()
MacroActionMacro::Action::ENABLE_ACTION || MacroActionMacro::Action::ENABLE_ACTION ||
_entryData->_action == MacroActionMacro::Action::TOGGLE_ACTION; _entryData->_action == MacroActionMacro::Action::TOGGLE_ACTION;
_actionIndex->setVisible(isModifyingActionState); _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 } // namespace advss

View File

@@ -3,7 +3,6 @@
#include "macro-selection.hpp" #include "macro-selection.hpp"
#include "macro-segment-selection.hpp" #include "macro-segment-selection.hpp"
#include <QCheckBox>
#include <QHBoxLayout> #include <QHBoxLayout>
namespace advss { namespace advss {
@@ -15,27 +14,12 @@ public:
void LogAction() const; void LogAction() const;
bool Save(obs_data_t *obj) const; bool Save(obs_data_t *obj) const;
bool Load(obs_data_t *obj); bool Load(obs_data_t *obj);
bool PostLoad();
std::string GetShortDesc() const; std::string GetShortDesc() const;
std::string GetId() const { return id; }; std::string GetId() const { return id; };
static std::shared_ptr<MacroAction> Create(Macro *m); static std::shared_ptr<MacroAction> Create(Macro *m);
std::shared_ptr<MacroAction> Copy() const; std::shared_ptr<MacroAction> Copy() const;
void ResolveVariablesToFixedValues(); 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 { enum class Action {
PAUSE, PAUSE,
UNPAUSE, UNPAUSE,
@@ -48,11 +32,8 @@ public:
}; };
Action _action = Action::PAUSE; Action _action = Action::PAUSE;
IntVariable _actionIndex = 1; IntVariable _actionIndex = 1;
RunOptions _runOptions = {};
private: private:
void RunActions(Macro *actionMacro) const;
static bool _registered; static bool _registered;
static const std::string id; static const std::string id;
}; };
@@ -77,28 +58,18 @@ private slots:
void MacroChanged(const QString &text); void MacroChanged(const QString &text);
void ActionChanged(int value); void ActionChanged(int value);
void ActionIndexChanged(const IntVariable &value); void ActionIndexChanged(const IntVariable &value);
void ConditionMacroChanged(const QString &text);
void ConditionBehaviorChanged(int value);
void ActionTypeChanged(int value);
void SkipWhenPausedChanged(int value);
signals: signals:
void HeaderInfoChanged(const QString &); void HeaderInfoChanged(const QString &);
protected:
MacroSelection *_macros;
MacroSegmentSelection *_actionIndex;
QComboBox *_actions;
std::shared_ptr<MacroActionMacro> _entryData;
private: private:
void SetWidgetVisibility(); 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; bool _loading = true;
}; };

View File

@@ -121,7 +121,8 @@ void DurationModifierEdit::Collapse(bool collapse)
MacroConditionEdit::MacroConditionEdit( MacroConditionEdit::MacroConditionEdit(
QWidget *parent, std::shared_ptr<MacroCondition> *entryData, QWidget *parent, std::shared_ptr<MacroCondition> *entryData,
const std::string &id, bool root) const std::string &id, bool root)
: MacroSegmentEdit(parent), : MacroSegmentEdit(GetGlobalMacroProperties()._highlightConditions,
parent),
_logicSelection(new QComboBox()), _logicSelection(new QComboBox()),
_conditionSelection(new FilterComboBox()), _conditionSelection(new FilterComboBox()),
_dur(new DurationModifierEdit()), _dur(new DurationModifierEdit()),
@@ -138,6 +139,8 @@ MacroConditionEdit::MacroConditionEdit(
QWidget::connect(_dur, SIGNAL(ModifierChanged(DurationModifier::Type)), QWidget::connect(_dur, SIGNAL(ModifierChanged(DurationModifier::Type)),
this, this,
SLOT(DurationModifierChanged(DurationModifier::Type))); SLOT(DurationModifierChanged(DurationModifier::Type)));
QWidget::connect(window(), SIGNAL(HighlightConditionsChanged(bool)),
this, SLOT(EnableHighlight(bool)));
populateLogicSelection(_logicSelection, root); populateLogicSelection(_logicSelection, root);
populateConditionSelection(_conditionSelection); populateConditionSelection(_conditionSelection);
@@ -296,7 +299,6 @@ void AdvSceneSwitcher::AddMacroCondition(int idx)
} }
if (idx < 0 || idx > (int)macro->Conditions().size()) { if (idx < 0 || idx > (int)macro->Conditions().size()) {
assert(false);
return; return;
} }
@@ -313,31 +315,16 @@ void AdvSceneSwitcher::AddMacroCondition(int idx)
id = MacroCondition::GetDefaultID(); id = MacroCondition::GetDefaultID();
logic = LogicType::ROOT_NONE; 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 lock = LockContext();
auto cond = macro->Conditions().emplace( auto cond = macro->Conditions().emplace(
macro->Conditions().begin() + idx, macro->Conditions().begin() + idx,
MacroConditionFactory::Create(id, macro)); MacroConditionFactory::Create(id, macro.get()));
if (data) { if (idx - 1 >= 0) {
auto data = obs_data_create();
macro->Conditions().at(idx - 1)->Save(data);
macro->Conditions().at(idx)->Load(data); macro->Conditions().at(idx)->Load(data);
obs_data_release(data);
} }
macro->Conditions().at(idx)->PostLoad(); macro->Conditions().at(idx)->PostLoad();
RunPostLoadSteps(); RunPostLoadSteps();
@@ -350,7 +337,6 @@ void AdvSceneSwitcher::AddMacroCondition(Macro *macro, int idx,
SetConditionData(*macro); SetConditionData(*macro);
} }
HighlightCondition(idx); HighlightCondition(idx);
ui->conditionsList->SetHelpMsgVisible(false);
emit(MacroSegmentOrderChanged()); emit(MacroSegmentOrderChanged());
} }
@@ -369,6 +355,7 @@ void AdvSceneSwitcher::on_conditionAdd_clicked()
if (currentConditionIdx != -1) { if (currentConditionIdx != -1) {
MacroConditionSelectionChanged(currentConditionIdx + 1); MacroConditionSelectionChanged(currentConditionIdx + 1);
} }
ui->conditionsList->SetHelpMsgVisible(false);
} }
void AdvSceneSwitcher::RemoveMacroCondition(int idx) void AdvSceneSwitcher::RemoveMacroCondition(int idx)

View File

@@ -1,104 +0,0 @@
#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

View File

@@ -1,8 +0,0 @@
namespace advss {
class AdvSceneSwitcher;
bool MacroSegmentIsInClipboard();
void SetupSegmentCopyPasteShortcutHandlers(AdvSceneSwitcher *window);
} // namespace advss

View File

@@ -176,8 +176,7 @@ bool MacroSegmentList::eventFilter(QObject *object, QEvent *event)
void MacroSegmentList::mousePressEvent(QMouseEvent *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) #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
_dragPosition = GetDragIndex(event->globalPos()); _dragPosition = GetDragIndex(event->globalPos());
#else #else

View File

@@ -59,12 +59,12 @@ std::string MacroSegment::GetShortDesc() const
return ""; return "";
} }
void MacroSegment::EnableHighlight() void MacroSegment::SetHighlight()
{ {
_highlight = true; _highlight = true;
} }
bool MacroSegment::GetHighlightAndReset() bool MacroSegment::Highlight()
{ {
if (_highlight) { if (_highlight) {
_highlight = false; _highlight = false;
@@ -194,7 +194,7 @@ void DecrementVariableRef(MacroSegment *segment)
segment->_variableRefs--; segment->_variableRefs--;
} }
MacroSegmentEdit::MacroSegmentEdit(QWidget *parent) MacroSegmentEdit::MacroSegmentEdit(bool highlight, QWidget *parent)
: QWidget(parent), : QWidget(parent),
_section(new Section(300)), _section(new Section(300)),
_headerInfo(new QLabel()), _headerInfo(new QLabel()),
@@ -203,7 +203,8 @@ MacroSegmentEdit::MacroSegmentEdit(QWidget *parent)
_noBorderframe(new QFrame), _noBorderframe(new QFrame),
_borderFrame(new QFrame), _borderFrame(new QFrame),
_dropLineAbove(new QFrame), _dropLineAbove(new QFrame),
_dropLineBelow(new QFrame) _dropLineBelow(new QFrame),
_showHighlight(highlight)
{ {
_dropLineAbove->setLineWidth(3); _dropLineAbove->setLineWidth(3);
_dropLineAbove->setFixedHeight(11); _dropLineAbove->setFixedHeight(11);
@@ -281,6 +282,10 @@ MacroSegmentEdit::MacroSegmentEdit(QWidget *parent)
// Enable dragging while clicking on the header text // Enable dragging while clicking on the header text
_headerInfo->installEventFilter(this); _headerInfo->installEventFilter(this);
_timer.setInterval(1500);
connect(&_timer, SIGNAL(timeout()), this, SLOT(Highlight()));
_timer.start();
} }
bool MacroSegmentEdit::eventFilter(QObject *obj, QEvent *ev) bool MacroSegmentEdit::eventFilter(QObject *obj, QEvent *ev)
@@ -325,6 +330,22 @@ 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() void MacroSegmentEdit::SetFocusPolicyOfWidgets()
{ {
QList<QWidget *> widgets = this->findChildren<QWidget *>(); QList<QWidget *> widgets = this->findChildren<QWidget *>();

View File

@@ -37,8 +37,8 @@ public:
virtual bool PostLoad(); virtual bool PostLoad();
virtual std::string GetShortDesc() const; virtual std::string GetShortDesc() const;
virtual std::string GetId() const = 0; virtual std::string GetId() const = 0;
void EnableHighlight(); void SetHighlight();
bool GetHighlightAndReset(); bool Highlight();
virtual std::string GetVariableValue() const; virtual std::string GetVariableValue() const;
protected: protected:
@@ -87,7 +87,7 @@ class MacroSegmentEdit : public QWidget {
Q_OBJECT Q_OBJECT
public: public:
MacroSegmentEdit(QWidget *parent = nullptr); MacroSegmentEdit(bool highlight, QWidget *parent = nullptr);
// Use this function to avoid accidental edits when scrolling through // Use this function to avoid accidental edits when scrolling through
// list of actions and conditions // list of actions and conditions
void SetFocusPolicyOfWidgets(); void SetFocusPolicyOfWidgets();
@@ -100,6 +100,8 @@ public slots:
protected slots: protected slots:
void Collapsed(bool); void Collapsed(bool);
void Highlight();
void EnableHighlight(bool);
signals: signals:
void MacroAdded(const QString &name); void MacroAdded(const QString &name);
void MacroRemoved(const QString &name); void MacroRemoved(const QString &name);
@@ -141,6 +143,9 @@ private:
QFrame *_dropLineAbove; QFrame *_dropLineAbove;
QFrame *_dropLineBelow; QFrame *_dropLineBelow;
bool _showHighlight;
QTimer _timer;
friend class MacroSegmentList; friend class MacroSegmentList;
}; };

View File

@@ -4,7 +4,6 @@
#include "macro-condition-edit.hpp" #include "macro-condition-edit.hpp"
#include "macro-export-import-dialog.hpp" #include "macro-export-import-dialog.hpp"
#include "macro-properties.hpp" #include "macro-properties.hpp"
#include "macro-segment-copy-paste.hpp"
#include "macro-tree.hpp" #include "macro-tree.hpp"
#include "macro.hpp" #include "macro.hpp"
#include "math-helpers.hpp" #include "math-helpers.hpp"
@@ -768,6 +767,8 @@ void AdvSceneSwitcher::on_macroProperties_clicked()
} }
GetGlobalMacroProperties() = prop; GetGlobalMacroProperties() = prop;
emit HighlightMacrosChanged(prop._highlightExecuted); emit HighlightMacrosChanged(prop._highlightExecuted);
emit HighlightActionsChanged(prop._highlightActions);
emit HighlightConditionsChanged(prop._highlightConditions);
} }
static void moveControlsToSplitter(QSplitter *splitter, int idx, static void moveControlsToSplitter(QSplitter *splitter, int idx,
@@ -782,7 +783,7 @@ static void moveControlsToSplitter(QSplitter *splitter, int idx,
splitter->setStyleSheet("QSplitter::handle {background: transparent;}"); splitter->setStyleSheet("QSplitter::handle {background: transparent;}");
} }
static bool shouldRestoreSplitter(const QList<int> &pos) bool shouldRestoreSplitter(const QList<int> &pos)
{ {
if (pos.size() == 0) { if (pos.size() == 0) {
return false; return false;
@@ -796,36 +797,6 @@ static bool shouldRestoreSplitter(const QList<int> &pos)
return true; 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() void AdvSceneSwitcher::SetupMacroTab()
{ {
ui->macroElseActions->installEventFilter(this); ui->macroElseActions->installEventFilter(this);
@@ -920,14 +891,6 @@ void AdvSceneSwitcher::SetupMacroTab()
switcher->macroListMacroEditSplitterPosition); 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) void AdvSceneSwitcher::ShowMacroContextMenu(const QPoint &pos)
@@ -1015,19 +978,16 @@ static void setupConextMenu(AdvSceneSwitcher *ss, const QPoint &pos,
MacroSegmentList *list) MacroSegmentList *list)
{ {
QMenu menu; 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 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) { if (segmentEdit) {
auto customLabel = menu.addAction(obs_module_text( auto customLabel = menu.addAction(obs_module_text(
"AdvSceneSwitcher.macroTab.segment.useCustomLabel")); "AdvSceneSwitcher.macroTab.segment.useCustomLabel"));
@@ -1039,21 +999,6 @@ static void setupConextMenu(AdvSceneSwitcher *ss, const QPoint &pos,
std::bind(handleCustomLabelChange, segmentEdit, std::bind(handleCustomLabelChange, segmentEdit,
customLabel)); 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)); menu.exec(list->mapToGlobal(pos));
} }
@@ -1398,47 +1343,58 @@ static void fade(QWidget *widget, bool fadeOut)
animation->start(QPropertyAnimation::DeleteWhenStopped); animation->start(QPropertyAnimation::DeleteWhenStopped);
} }
void fadeWidgets(const std::vector<QWidget *> &widgets, bool fadeOut) void AdvSceneSwitcher::FadeOutActionControls()
{ {
for (const auto &widget : widgets) { fade(ui->actionAdd, true);
fade(widget, fadeOut); 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);
} }
void AdvSceneSwitcher::HighlightControls() void AdvSceneSwitcher::HighlightControls()
{ {
const std::vector<QWidget *> conditionControls{ if ((currentActionIdx == -1 && currentConditionIdx == -1) ||
ui->conditionAdd, ui->conditionRemove, ui->conditionTop, (currentActionIdx != -1 && currentConditionIdx != -1)) {
ui->conditionUp, ui->conditionDown, ui->conditionBottom, ResetOpacityActionControls();
}; ResetOpacityConditionControls();
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) { } else if (currentActionIdx != -1) {
fadeWidgets(conditionControls, true); FadeOutConditionControls();
fadeWidgets(actionControls, false); ResetOpacityActionControls();
fadeWidgets(elseActionControls, true);
} else if (currentElseActionIdx != -1) {
fadeWidgets(conditionControls, true);
fadeWidgets(actionControls, true);
fadeWidgets(elseActionControls, false);
} else { } else {
assert(false); FadeOutActionControls();
ResetOpacityConditionControls();
} }
} }

View File

@@ -137,37 +137,37 @@ bool Macro::CeckMatch()
case LogicType::AND: case LogicType::AND:
_matched = _matched && cond; _matched = _matched && cond;
if (cond) { if (cond) {
c->EnableHighlight(); c->SetHighlight();
} }
break; break;
case LogicType::OR: case LogicType::OR:
_matched = _matched || cond; _matched = _matched || cond;
if (cond) { if (cond) {
c->EnableHighlight(); c->SetHighlight();
} }
break; break;
case LogicType::AND_NOT: case LogicType::AND_NOT:
_matched = _matched && !cond; _matched = _matched && !cond;
if (!cond) { if (!cond) {
c->EnableHighlight(); c->SetHighlight();
} }
break; break;
case LogicType::OR_NOT: case LogicType::OR_NOT:
_matched = _matched || !cond; _matched = _matched || !cond;
if (!cond) { if (!cond) {
c->EnableHighlight(); c->SetHighlight();
} }
break; break;
case LogicType::ROOT_NONE: case LogicType::ROOT_NONE:
_matched = cond; _matched = cond;
if (cond) { if (cond) {
c->EnableHighlight(); c->SetHighlight();
} }
break; break;
case LogicType::ROOT_NOT: case LogicType::ROOT_NOT:
_matched = !cond; _matched = !cond;
if (!cond) { if (!cond) {
c->EnableHighlight(); c->SetHighlight();
} }
break; break;
default: default:
@@ -313,7 +313,7 @@ bool Macro::RunActionsHelper(
break; break;
} }
if (action->Enabled()) { if (action->Enabled()) {
action->EnableHighlight(); action->SetHighlight();
} }
} }
_done = true; _done = true;
@@ -819,10 +819,10 @@ void Macro::ResetUIHelpers()
{ {
_onPreventedActionExecution = false; _onPreventedActionExecution = false;
for (auto c : _conditions) { for (auto c : _conditions) {
c->GetHighlightAndReset(); c->Highlight();
} }
for (auto a : _actions) { for (auto a : _actions) {
a->GetHighlightAndReset(); a->Highlight();
} }
} }

View File

@@ -67,21 +67,7 @@ bool MacroConditionAudio::CheckOutputCondition()
OBSSourceAutoRelease source = OBSSourceAutoRelease source =
obs_weak_source_get_source(_audioSource.GetSource()); obs_weak_source_get_source(_audioSource.GetSource());
// OBS might very rarely not update _peak quickly enough when very low double curVolume = _useDb ? _peak : DecibelToPercent(_peak) * 100;
// 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.
float peak;
{
std::lock_guard<std::mutex> lock(_peakMutex);
peak = _peakUpdated ? _peak : _previousPeak;
_previousPeak = peak;
_peak = -std::numeric_limits<float>::infinity();
_peakUpdated = false;
}
double curVolume = _useDb ? peak : DecibelToPercent(peak) * 100;
switch (_outputCondition) { switch (_outputCondition) {
case OutputCondition::ABOVE: case OutputCondition::ABOVE:
@@ -106,6 +92,7 @@ bool MacroConditionAudio::CheckOutputCondition()
SetTempVarValue("output_volume", std::to_string(curVolume)); SetTempVarValue("output_volume", std::to_string(curVolume));
// Reset for next check // Reset for next check
_peak = -std::numeric_limits<float>::infinity();
if (_audioSource.GetType() == SourceSelection::Type::VARIABLE) { if (_audioSource.GetType() == SourceSelection::Type::VARIABLE) {
ResetVolmeter(); ResetVolmeter();
} }
@@ -342,13 +329,11 @@ void MacroConditionAudio::SetVolumeLevel(void *data, const float *,
return; return;
} }
std::lock_guard<std::mutex> lock(c->_peakMutex);
for (int i = 0; i < MAX_AUDIO_CHANNELS; i++) { for (int i = 0; i < MAX_AUDIO_CHANNELS; i++) {
if (peak[i] > c->_peak) { if (peak[i] > c->_peak) {
c->_peak = peak[i]; c->_peak = peak[i];
} }
} }
c->_peakUpdated = true;
} }
void MacroConditionAudio::ResetVolmeter() void MacroConditionAudio::ResetVolmeter()

View File

@@ -73,10 +73,7 @@ private:
void SetupTempVars(); void SetupTempVars();
Type _checkType = Type::OUTPUT_VOLUME; Type _checkType = Type::OUTPUT_VOLUME;
std::mutex _peakMutex;
float _peak = -std::numeric_limits<float>::infinity(); float _peak = -std::numeric_limits<float>::infinity();
float _previousPeak = -std::numeric_limits<float>::infinity();
bool _peakUpdated = false;
static bool _registered; static bool _registered;
static const std::string id; static const std::string id;
}; };

View File

@@ -45,13 +45,11 @@ static bool setupReplayBufferEventHandler()
bool MacroConditionReplayBuffer::ReplayBufferWasSaved() bool MacroConditionReplayBuffer::ReplayBufferWasSaved()
{ {
if (!_saveTimeInitialized) { bool timersAreInitialized =
_saveTime = replayBufferSaveTime; _saveTime.time_since_epoch().count() != 0 &&
_saveTimeInitialized = true; replayBufferSaveTime.time_since_epoch().count() != 0;
return false; bool newSaveOccurred = timersAreInitialized &&
} _saveTime != replayBufferSaveTime;
bool newSaveOccurred = _saveTime != replayBufferSaveTime;
_saveTime = replayBufferSaveTime; _saveTime = replayBufferSaveTime;
return newSaveOccurred; return newSaveOccurred;
} }

View File

@@ -29,7 +29,6 @@ public:
private: private:
bool ReplayBufferWasSaved(); bool ReplayBufferWasSaved();
bool _saveTimeInitialized = false;
std::chrono::high_resolution_clock::time_point _saveTime = {}; std::chrono::high_resolution_clock::time_point _saveTime = {};
static bool _registered; static bool _registered;
static const std::string id; static const std::string id;

View File

@@ -175,43 +175,22 @@ void PressKeys(const std::vector<HotkeyType> keys, int duration)
} }
} }
static bool windowIsValid(HWND window) static HWND getHWNDfromTitle(const std::string &title)
{ {
if (!IsWindowVisible(window)) { HWND hwnd = NULL;
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]; wchar_t wTitle[512];
os_utf8_to_wcs(title.c_str(), 0, wTitle, 512); os_utf8_to_wcs(title.c_str(), 0, wTitle, 512);
while ((hwnd = FindWindowEx(NULL, hPrevWnd, NULL, wTitle)) != nullptr) { hwnd = FindWindowEx(NULL, NULL, NULL, wTitle);
if (windowIsValid(hwnd)) { return hwnd;
hwnds.emplace_back(hwnd);
}
hPrevWnd = hwnd;
}
return hwnds;
} }
std::string GetWindowClassByWindowTitle(const std::string &window) std::string GetWindowClassByWindowTitle(const std::string &window)
{ {
auto hwnds = getHWNDfromTitle(window); HWND hwnd = NULL;
if (hwnds.empty()) { hwnd = getHWNDfromTitle(window);
if (!hwnd) {
return ""; return "";
} }
auto hwnd = hwnds.at(0);
std::wstring wClass; std::wstring wClass;
wClass.resize(1024); wClass.resize(1024);
if (!GetClassNameW(hwnd, &wClass[0], wClass.capacity())) { if (!GetClassNameW(hwnd, &wClass[0], wClass.capacity())) {
@@ -227,54 +206,41 @@ std::string GetWindowClassByWindowTitle(const std::string &window)
void SetFocusWindow(const std::string &title) void SetFocusWindow(const std::string &title)
{ {
auto hwnds = getHWNDfromTitle(title); auto handle = getHWNDfromTitle(title);
if (hwnds.empty()) { if (!handle) {
return; return;
} }
ShowWindow(handle, SW_RESTORE);
for (const auto &handle : hwnds) { SetForegroundWindow(handle);
const bool isMinimized = IsIconic(handle); SetFocus(handle);
ShowWindow(handle, isMinimized ? SW_RESTORE : SW_SHOW); SetActiveWindow(handle);
SetForegroundWindow(handle);
SetFocus(handle);
SetActiveWindow(handle);
}
} }
void CloseWindow(const std::string &title) void CloseWindow(const std::string &title)
{ {
auto hwnds = getHWNDfromTitle(title); auto handle = getHWNDfromTitle(title);
if (hwnds.empty()) { if (!handle) {
return; return;
} }
SendMessage(handle, WM_CLOSE, 0, 0);
for (const auto &handle : hwnds) {
SendMessage(handle, WM_CLOSE, 0, 0);
}
} }
void MaximizeWindow(const std::string &title) void MaximizeWindow(const std::string &title)
{ {
auto hwnds = getHWNDfromTitle(title); auto handle = getHWNDfromTitle(title);
if (hwnds.empty()) { if (!handle) {
return; return;
} }
ShowWindow(handle, SW_MAXIMIZE);
for (const auto &handle : hwnds) {
ShowWindow(handle, SW_MAXIMIZE);
}
} }
void MinimizeWindow(const std::string &title) void MinimizeWindow(const std::string &title)
{ {
auto hwnds = getHWNDfromTitle(title); auto handle = getHWNDfromTitle(title);
if (hwnds.empty()) { if (!handle) {
return; return;
} }
ShowWindow(handle, SW_MINIMIZE);
for (const auto &handle : hwnds) {
ShowWindow(handle, SW_MINIMIZE);
}
} }
class RawMouseInputFilter; class RawMouseInputFilter;

View File

@@ -12,7 +12,73 @@ bool MacroConditionMidi::_registered = MacroConditionFactory::Register(
{MacroConditionMidi::Create, MacroConditionMidiEdit::Create, {MacroConditionMidi::Create, MacroConditionMidiEdit::Create,
"AdvSceneSwitcher.condition.midi"}); "AdvSceneSwitcher.condition.midi"});
static const std::map<MacroConditionMidi::Condition, std::string>
conditionTypes = {
{MacroConditionMidi::Condition::MIDI_MESSAGE,
"AdvSceneSwitcher.condition.midi.condition.message"},
{MacroConditionMidi::Condition::MIDI_CONNECTED_DEVICE_NAMES,
"AdvSceneSwitcher.condition.midi.condition.deviceName"},
};
bool MacroConditionMidi::CheckCondition() bool MacroConditionMidi::CheckCondition()
{
switch (_condition) {
case Condition::MIDI_MESSAGE:
return CheckMessage();
case Condition::MIDI_CONNECTED_DEVICE_NAMES:
return CheckConnectedDevcieNames();
default:
break;
}
return false;
}
bool MacroConditionMidi::Save(obs_data_t *obj) const
{
MacroCondition::Save(obj);
obs_data_set_int(obj, "condition", static_cast<int>(_condition));
_message.Save(obj);
_device.Save(obj);
_deviceName.Save(obj, "deviceName");
_regex.Save(obj);
return true;
}
bool MacroConditionMidi::Load(obs_data_t *obj)
{
MacroCondition::Load(obj);
_message.Load(obj);
_device.Load(obj);
_deviceName.Load(obj, "deviceName");
_regex.Load(obj);
SetCondition(
static_cast<Condition>(obs_data_get_int(obj, "condition")));
return true;
}
std::string MacroConditionMidi::GetShortDesc() const
{
return _condition == MacroConditionMidi::Condition::MIDI_MESSAGE
? _device.Name()
: "";
}
void MacroConditionMidi::SetDevice(const MidiDevice &dev)
{
_device = dev;
_messageBuffer = dev.RegisterForMidiMessages();
}
void MacroConditionMidi::SetCondition(Condition condition)
{
_condition = condition;
if (_condition == Condition::MIDI_MESSAGE) {
_messageBuffer = _device.RegisterForMidiMessages();
}
SetupTempVars();
}
bool MacroConditionMidi::CheckMessage()
{ {
if (!_messageBuffer) { if (!_messageBuffer) {
return false; return false;
@@ -40,37 +106,27 @@ bool MacroConditionMidi::CheckCondition()
return false; return false;
} }
bool MacroConditionMidi::Save(obs_data_t *obj) const bool MacroConditionMidi::CheckConnectedDevcieNames()
{ {
MacroCondition::Save(obj); auto deviceNames = GetDeviceNames();
_message.Save(obj); if (!_regex.Enabled()) {
_device.Save(obj); return std::find(deviceNames.begin(), deviceNames.end(),
return true; std::string(_deviceName)) != deviceNames.end();
} }
for (const auto &deviceName : deviceNames) {
bool MacroConditionMidi::Load(obs_data_t *obj) if (_regex.Matches(deviceName, _deviceName)) {
{ return true;
MacroCondition::Load(obj); }
_message.Load(obj); }
_device.Load(obj); return false;
_messageBuffer = _device.RegisterForMidiMessages();
return true;
}
std::string MacroConditionMidi::GetShortDesc() const
{
return _device.Name();
}
void MacroConditionMidi::SetDevice(const MidiDevice &dev)
{
_device = dev;
_messageBuffer = dev.RegisterForMidiMessages();
} }
void MacroConditionMidi::SetupTempVars() void MacroConditionMidi::SetupTempVars()
{ {
MacroCondition::SetupTempVars(); MacroCondition::SetupTempVars();
if (_condition == Condition::MIDI_CONNECTED_DEVICE_NAMES) {
return;
}
AddTempvar("type", AddTempvar("type",
obs_module_text("AdvSceneSwitcher.tempVar.midi.type")); obs_module_text("AdvSceneSwitcher.tempVar.midi.type"));
AddTempvar("channel", AddTempvar("channel",
@@ -98,16 +154,45 @@ void MacroConditionMidi::SetVariableValues(const MidiMessage &m)
SetTempVarValue("value2", std::to_string(m.Value())); SetTempVarValue("value2", std::to_string(m.Value()));
} }
static void populateConditionSelection(QComboBox *list)
{
for (const auto &[_, name] : conditionTypes) {
list->addItem(obs_module_text(name.c_str()));
}
}
MacroConditionMidiEdit::MacroConditionMidiEdit( MacroConditionMidiEdit::MacroConditionMidiEdit(
QWidget *parent, std::shared_ptr<MacroConditionMidi> entryData) QWidget *parent, std::shared_ptr<MacroConditionMidi> entryData)
: QWidget(parent), : QWidget(parent),
_conditions(new QComboBox()),
_devices(new MidiDeviceSelection(this, MidiDeviceType::INPUT)), _devices(new MidiDeviceSelection(this, MidiDeviceType::INPUT)),
_message(new MidiMessageSelection(this)), _message(new MidiMessageSelection(this)),
_resetMidiDevices(new QPushButton( _resetMidiDevices(new QPushButton(
obs_module_text("AdvSceneSwitcher.midi.resetDevices"))), obs_module_text("AdvSceneSwitcher.midi.resetDevices"))),
_listen(new QPushButton( _listen(new QPushButton(
obs_module_text("AdvSceneSwitcher.midi.startListen"))) obs_module_text("AdvSceneSwitcher.midi.startListen"))),
_deviceNames(new QComboBox()),
_regex(new RegexConfigWidget()),
_deviceListInfo(new QLabel()),
_listenLayout(new QHBoxLayout()),
_entryLayout(new QHBoxLayout())
{ {
_deviceNames->addItems(GetDeviceNamesAsQStringList());
_deviceNames->setEditable(true);
QString path = GetThemeTypeName() == "Light"
? ":/res/images/help.svg"
: ":/res/images/help_light.svg";
QIcon icon(path);
QPixmap pixmap = icon.pixmap(QSize(16, 16));
_deviceListInfo->setPixmap(pixmap);
_deviceListInfo->setToolTip(obs_module_text(
"AdvSceneSwitcher.condition.midi.condition.deviceName.info"));
populateConditionSelection(_conditions);
QWidget::connect(_conditions, SIGNAL(currentIndexChanged(int)), this,
SLOT(ConditionChanged(int)));
QWidget::connect(_devices, QWidget::connect(_devices,
SIGNAL(DeviceSelectionChanged(const MidiDevice &)), SIGNAL(DeviceSelectionChanged(const MidiDevice &)),
this, this,
@@ -121,19 +206,21 @@ MacroConditionMidiEdit::MacroConditionMidiEdit(
SLOT(ToggleListen())); SLOT(ToggleListen()));
QWidget::connect(&_listenTimer, SIGNAL(timeout()), this, QWidget::connect(&_listenTimer, SIGNAL(timeout()), this,
SLOT(SetMessageSelectionToLastReceived())); SLOT(SetMessageSelectionToLastReceived()));
QWidget::connect(_deviceNames,
SIGNAL(currentTextChanged(const QString &)), this,
SLOT(DeviceNameChanged(const QString &)));
QWidget::connect(_regex,
SIGNAL(RegexConfigChanged(const RegexConfig &)), this,
SLOT(RegexChanged(const RegexConfig &)));
auto entryLayout = new QHBoxLayout;
PlaceWidgets(obs_module_text("AdvSceneSwitcher.condition.midi.entry"),
entryLayout, {{"{{device}}", _devices}});
auto listenLayout = new QHBoxLayout;
PlaceWidgets( PlaceWidgets(
obs_module_text("AdvSceneSwitcher.condition.midi.entry.listen"), obs_module_text("AdvSceneSwitcher.condition.midi.entry.listen"),
listenLayout, {{"{{listenButton}}", _listen}}); _listenLayout, {{"{{listenButton}}", _listen}});
auto mainLayout = new QVBoxLayout; auto mainLayout = new QVBoxLayout;
mainLayout->addLayout(entryLayout); mainLayout->addLayout(_entryLayout);
mainLayout->addWidget(_message); mainLayout->addWidget(_message);
mainLayout->addLayout(listenLayout); mainLayout->addLayout(_listenLayout);
mainLayout->addWidget(_resetMidiDevices); mainLayout->addWidget(_resetMidiDevices);
setLayout(mainLayout); setLayout(mainLayout);
@@ -155,11 +242,14 @@ void MacroConditionMidiEdit::UpdateEntryData()
return; return;
} }
_conditions->setCurrentIndex(
static_cast<int>(_entryData->GetCondition()));
_message->SetMessage(_entryData->_message); _message->SetMessage(_entryData->_message);
_devices->SetDevice(_entryData->GetDevice()); _devices->SetDevice(_entryData->GetDevice());
_deviceNames->setCurrentText(QString::fromStdString(
adjustSize(); _entryData->_deviceName.UnresolvedValue()));
updateGeometry(); _regex->SetRegexConfig(_entryData->_regex);
SetWidgetVisibility();
} }
void MacroConditionMidiEdit::DeviceSelectionChanged(const MidiDevice &device) void MacroConditionMidiEdit::DeviceSelectionChanged(const MidiDevice &device)
@@ -211,6 +301,46 @@ void MacroConditionMidiEdit::EnableListening(bool enable)
} }
} }
void MacroConditionMidiEdit::SetWidgetVisibility()
{
_entryLayout->removeWidget(_conditions);
_entryLayout->removeWidget(_devices);
_entryLayout->removeWidget(_deviceNames);
_entryLayout->removeWidget(_regex);
ClearLayout(_entryLayout);
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
{"{{conditions}}", _conditions},
{"{{device}}", _devices},
{"{{deviceNames}}", _deviceNames},
{"{{regex}}", _regex},
{"{{deviceListInfo}}", _deviceListInfo},
};
const bool isMessageCheck = _entryData->GetCondition() ==
MacroConditionMidi::Condition::MIDI_MESSAGE;
auto layoutString =
isMessageCheck
? "AdvSceneSwitcher.condition.midi.entry.message"
: "AdvSceneSwitcher.condition.midi.entry.deviceName";
PlaceWidgets(obs_module_text(layoutString), _entryLayout,
widgetPlaceholders);
SetLayoutVisible(_listenLayout, isMessageCheck);
_devices->setVisible(isMessageCheck);
_message->setVisible(isMessageCheck);
_resetMidiDevices->setVisible(isMessageCheck);
_deviceNames->setVisible(!isMessageCheck);
_regex->setVisible(!isMessageCheck);
_deviceListInfo->setVisible(!isMessageCheck);
adjustSize();
updateGeometry();
}
void MacroConditionMidiEdit::ToggleListen() void MacroConditionMidiEdit::ToggleListen()
{ {
if (!_entryData) { if (!_entryData) {
@@ -249,4 +379,41 @@ void MacroConditionMidiEdit::SetMessageSelectionToLastReceived()
_entryData->_message = *message; _entryData->_message = *message;
} }
void MacroConditionMidiEdit::ConditionChanged(int value)
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
_entryData->SetCondition(
static_cast<MacroConditionMidi::Condition>(value));
SetWidgetVisibility();
emit HeaderInfoChanged(
QString::fromStdString(_entryData->GetShortDesc()));
}
void MacroConditionMidiEdit::DeviceNameChanged(const QString &deviceName)
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
_entryData->_deviceName = deviceName.toStdString();
}
void MacroConditionMidiEdit::RegexChanged(const RegexConfig &conf)
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
_entryData->_regex = conf;
adjustSize();
updateGeometry();
}
} // namespace advss } // namespace advss

View File

@@ -1,6 +1,8 @@
#pragma once #pragma once
#include "macro-condition-edit.hpp" #include "macro-condition-edit.hpp"
#include "midi-helpers.hpp" #include "midi-helpers.hpp"
#include "regex-config.hpp"
#include "variable-line-edit.hpp"
#include <QPushButton> #include <QPushButton>
#include <QTimer> #include <QTimer>
@@ -23,11 +25,23 @@ public:
void SetDevice(const MidiDevice &dev); void SetDevice(const MidiDevice &dev);
const MidiDevice &GetDevice() const { return _device; } const MidiDevice &GetDevice() const { return _device; }
MidiMessage _message; MidiMessage _message;
StringVariable _deviceName;
RegexConfig _regex;
enum class Condition {
MIDI_MESSAGE,
MIDI_CONNECTED_DEVICE_NAMES,
};
void SetCondition(Condition);
Condition GetCondition() const { return _condition; }
private: private:
bool CheckMessage();
bool CheckConnectedDevcieNames();
void SetupTempVars(); void SetupTempVars();
void SetVariableValues(const MidiMessage &); void SetVariableValues(const MidiMessage &);
Condition _condition = Condition::MIDI_MESSAGE;
MidiDevice _device; MidiDevice _device;
MidiMessageBuffer _messageBuffer; MidiMessageBuffer _messageBuffer;
std::chrono::high_resolution_clock::time_point _lastCheck{}; std::chrono::high_resolution_clock::time_point _lastCheck{};
@@ -58,18 +72,29 @@ private slots:
void ResetMidiDevices(); void ResetMidiDevices();
void ToggleListen(); void ToggleListen();
void SetMessageSelectionToLastReceived(); void SetMessageSelectionToLastReceived();
void ConditionChanged(int);
void DeviceNameChanged(const QString &);
void RegexChanged(const RegexConfig &);
signals: signals:
void HeaderInfoChanged(const QString &); void HeaderInfoChanged(const QString &);
private: private:
void EnableListening(bool); void EnableListening(bool);
void SetWidgetVisibility();
std::shared_ptr<MacroConditionMidi> _entryData; std::shared_ptr<MacroConditionMidi> _entryData;
QComboBox *_conditions;
MidiDeviceSelection *_devices; MidiDeviceSelection *_devices;
MidiMessageSelection *_message; MidiMessageSelection *_message;
QPushButton *_resetMidiDevices; QPushButton *_resetMidiDevices;
QPushButton *_listen; QPushButton *_listen;
QComboBox *_deviceNames;
RegexConfigWidget *_regex;
QLabel *_deviceListInfo;
QHBoxLayout *_listenLayout;
QHBoxLayout *_entryLayout;
QTimer _listenTimer; QTimer _listenTimer;
MidiMessageBuffer _messageBuffer; MidiMessageBuffer _messageBuffer;
bool _currentlyListening = false; bool _currentlyListening = false;

View File

@@ -20,56 +20,47 @@ std::map<std::pair<MidiDeviceType, std::string>, MidiDeviceInstance *>
static bool setupDeviceObservers() static bool setupDeviceObservers()
{ {
static std::vector<libremidi::observer> observers; static std::vector<libremidi::observer> observers;
try { for (auto api : libremidi::available_apis()) {
for (auto api : libremidi::available_apis()) { libremidi::observer_configuration cbs;
libremidi::observer_configuration cbs; cbs.track_virtual = true;
cbs.input_added = [=](const libremidi::input_port &p) { cbs.input_added = [](const libremidi::input_port &p) {
auto dev = MidiDeviceInstance::GetDevice(p); auto dev = MidiDeviceInstance::GetDevice(p);
if (!dev) { if (!dev) {
return; return;
} }
blog(LOG_INFO, "MIDI input connected: %s", blog(LOG_INFO, "MIDI input connected: %s",
p.port_name.c_str()); p.port_name.c_str());
dev->ClosePort(); dev->ClosePort();
dev->OpenPort(); dev->OpenPort();
}; };
cbs.input_removed = [=](const libremidi::input_port &p) { cbs.input_removed = [](const libremidi::input_port &p) {
auto dev = MidiDeviceInstance::GetDevice(p); auto dev = MidiDeviceInstance::GetDevice(p);
if (!dev) { if (!dev) {
return; return;
} }
blog(LOG_INFO, "MIDI input removed: %s", blog(LOG_INFO, "MIDI input removed: %s",
p.port_name.c_str()); p.port_name.c_str());
}; };
cbs.output_added = [=](const libremidi::output_port &p) { cbs.output_added = [](const libremidi::output_port &p) {
auto dev = MidiDeviceInstance::GetDevice(p); auto dev = MidiDeviceInstance::GetDevice(p);
if (!dev) { if (!dev) {
return; return;
} }
blog(LOG_INFO, "MIDI output connected: %s", blog(LOG_INFO, "MIDI output connected: %s",
p.port_name.c_str()); p.port_name.c_str());
dev->ClosePort(); dev->ClosePort();
dev->OpenPort(); dev->OpenPort();
}; };
cbs.output_removed = cbs.output_removed = [](const libremidi::output_port &p) {
[=](const libremidi::output_port &p) { auto dev = MidiDeviceInstance::GetDevice(p);
auto dev = if (!dev) {
MidiDeviceInstance::GetDevice( return;
p); }
if (!dev) { blog(LOG_INFO, "MIDI output removed: %s",
return; p.port_name.c_str());
} };
blog(LOG_INFO, observers.emplace_back(
"MIDI output removed: %s", cbs, libremidi::observer_configuration_for(api));
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; return true;
} }
@@ -338,35 +329,95 @@ advss::MidiDeviceInstance::GetDevice(const libremidi::output_port &p)
static inline QStringList getInputDeviceNames() static inline QStringList getInputDeviceNames()
{ {
QStringList devices; static QStringList devices;
static std::mutex m;
static std::vector<libremidi::observer> observers;
static bool setupDone = false;
if (setupDone) {
std::lock_guard<std::mutex> lock(m);
return devices;
}
// Set up observers
try { try {
libremidi::observer obs; for (auto api : libremidi::available_apis()) {
for (const libremidi::input_port &port : libremidi::observer_configuration cbs;
obs.get_input_ports()) { cbs.track_virtual = true;
devices << QString::fromStdString( cbs.input_added = [](const libremidi::input_port &p) {
getNameFromPortInformation(port)); std::lock_guard<std::mutex> lock(m);
auto device = QString::fromStdString(
getNameFromPortInformation(p));
if (devices.indexOf(device) == -1) {
devices << device;
}
};
cbs.input_removed = [](const libremidi::input_port &p) {
std::lock_guard<std::mutex> lock(m);
auto device = QString::fromStdString(
getNameFromPortInformation(p));
if (int i = devices.indexOf(device) != -1) {
devices.removeAt(i);
}
};
observers.emplace_back(
cbs,
libremidi::observer_configuration_for(api));
} }
} catch (const libremidi::driver_error &error) { } catch (const libremidi::driver_error &error) {
blog(LOG_WARNING, "Failed to get midi input devices: %s", blog(LOG_WARNING, "Failed to get midi input devices: %s",
error.what()); error.what());
} }
setupDone = true;
return devices; return devices;
} }
static inline QStringList getOutputDeviceNames() static inline QStringList getOutputDeviceNames()
{ {
QStringList devices; static QStringList devices;
static std::mutex m;
static std::vector<libremidi::observer> observers;
static bool setupDone = false;
if (setupDone) {
std::lock_guard<std::mutex> lock(m);
return devices;
}
// Set up observers
try { try {
libremidi::observer obs; for (auto api : libremidi::available_apis()) {
for (const libremidi::output_port &port : libremidi::observer_configuration cbs;
obs.get_output_ports()) { cbs.track_virtual = true;
devices << QString::fromStdString( cbs.output_added = [](const libremidi::output_port &p) {
getNameFromPortInformation(port)); std::lock_guard<std::mutex> lock(m);
auto device = QString::fromStdString(
getNameFromPortInformation(p));
if (devices.indexOf(device) == -1) {
devices << device;
}
};
cbs.output_removed =
[](const libremidi::output_port &p) {
std::lock_guard<std::mutex> lock(m);
auto device = QString::fromStdString(
getNameFromPortInformation(p));
if (int i = devices.indexOf(device) !=
-1) {
devices.removeAt(i);
}
};
observers.emplace_back(
cbs,
libremidi::observer_configuration_for(api));
} }
} catch (const libremidi::driver_error &error) { } catch (const libremidi::driver_error &error) {
blog(LOG_WARNING, "Failed to get midi output devices: %s", blog(LOG_WARNING, "Failed to get midi output devices: %s",
error.what()); error.what());
} }
setupDone = true;
return devices; return devices;
} }
@@ -434,16 +485,11 @@ bool MidiDevice::SendMessge(const MidiMessage &m) const
static std::optional<libremidi::output_port> static std::optional<libremidi::output_port>
getOutPortFromName(const std::string &name) getOutPortFromName(const std::string &name)
{ {
try { libremidi::observer obs;
libremidi::observer obs; for (const libremidi::output_port &port : obs.get_output_ports()) {
for (const libremidi::output_port &port : if (getNameFromPortInformation(port) == name) {
obs.get_output_ports()) { return port;
if (getNameFromPortInformation(port) == name) {
return port;
}
} }
} catch (const libremidi::driver_error &error) {
blog(LOG_WARNING, "%s: %s", __func__, error.what());
} }
return {}; return {};
} }
@@ -451,16 +497,11 @@ getOutPortFromName(const std::string &name)
static std::optional<libremidi::input_port> static std::optional<libremidi::input_port>
getInPortFromName(const std::string &name) getInPortFromName(const std::string &name)
{ {
try { libremidi::observer obs;
libremidi::observer obs; for (const libremidi::input_port &port : obs.get_input_ports()) {
for (const libremidi::input_port &port : if (getNameFromPortInformation(port) == name) {
obs.get_input_ports()) { return port;
if (getNameFromPortInformation(port) == name) {
return port;
}
} }
} catch (const libremidi::driver_error &error) {
blog(LOG_WARNING, "%s: %s", __func__, error.what());
} }
return {}; return {};
} }
@@ -1017,6 +1058,28 @@ void MidiMessageSelection::ValueChanged(const NumberVariable<int> &v)
emit MidiMessageChanged((_currentSelection)); emit MidiMessageChanged((_currentSelection));
} }
std::vector<std::string> GetDeviceNames()
{
std::vector<std::string> result;
const auto names = GetDeviceNamesAsQStringList();
for (const auto &name : names) {
result.emplace_back(name.toStdString());
}
return result;
}
QStringList GetDeviceNamesAsQStringList()
{
QStringList result;
for (const auto &input : getInputDeviceNames()) {
result << input;
}
for (const auto &output : getOutputDeviceNames()) {
result << output;
}
return result;
}
QStringList GetAllNotes() QStringList GetAllNotes()
{ {
static bool done = false; static bool done = false;

View File

@@ -171,6 +171,8 @@ private:
const MidiDeviceType _type; const MidiDeviceType _type;
}; };
std::vector<std::string> GetDeviceNames();
QStringList GetDeviceNamesAsQStringList();
QStringList GetAllNotes(); QStringList GetAllNotes();
} // namespace advss } // namespace advss

View File

@@ -29,12 +29,6 @@ static void setTabVisible(QTabWidget *tabWidget, bool visible)
obs_module_text("AdvSceneSwitcher.twitchConnectionTab.title")); 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() TwitchConnectionsTable *TwitchConnectionsTable::Create()
{ {
tabWidget = new TwitchConnectionsTable(); tabWidget = new TwitchConnectionsTable();
@@ -217,11 +211,11 @@ static void setupTab(QTabWidget *tab)
} }
}); });
QWidget::connect(tab, &QTabWidget::currentChanged, [tab]() { auto timer = new QTimer(tabWidget);
if (twitchTabIsSelected(tab)) { timer->setInterval(1000);
updateConnectionStatus(tabWidget->Table()); QWidget::connect(timer, &QTimer::timeout,
} []() { updateConnectionStatus(tabWidget->Table()); });
}); timer->start();
} }
} // namespace advss } // namespace advss