Compare commits

..

13 Commits
midi ... 1.26.1

Author SHA1 Message Date
WarmUpTill
cc4a3560d8 Fix "Window" actions not being applied to OBS windows 2024-05-12 01:29:06 +02:00
WarmUpTill
3ab2ea9e66 Only update Twitch tab content when switching to it
This should reduce the potential lag introduced when querying the token
status using the Twitch API
2024-05-12 01:28:51 +02:00
WarmUpTill
69b82ee2a3 Fix audio condition incorrectly reporting very low volume levels
OBS might very 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.
2024-05-10 15:11:20 +02:00
WarmUpTill
f14e6bf252 Fix crash when access to midi devices is denied 2024-05-10 15:11:20 +02:00
WarmUpTill
e51ae79b4c Improve window action
* The focus action no longer restores maximized windows
* The focus, maximize, minimize, and close actions will be performed on
  all handles matching the given window title
2024-05-10 15:11:20 +02:00
WarmUpTill
8f55856fd5 Fix macro segment highlighting not being in sync
Each macro segment had its own internal timer to handle highlighting.
This handing was now moved centrally to the macro tab.
2024-05-10 15:11:20 +02:00
WarmUpTill
a360d53419 Enable right-click to select macro segments
This will be used to enable copy-paste via the context menu available
when right-clicking a macro segment
2024-05-10 15:11:20 +02:00
WarmUpTill
d28164d02f Fix initial replay buffer save not triggering condition 2024-05-10 15:11:20 +02:00
WarmUpTill
f723212394 Add copy-paste support for individual macro segments 2024-05-10 15:11:20 +02:00
WarmUpTill
a0cb08d18f Fix potential crash when creating actions 2024-05-10 15:11:20 +02:00
WarmUpTill
d18044b764 Add handling for fading of else-controls 2024-05-10 15:11:20 +02:00
WarmUpTill
406b5d61f5 Add option to conditionally run action of other macros 2024-05-10 15:11:20 +02:00
WarmUpTill
9a4eef4a83 Adjust busy loop sleep to 10ms
This is now in line with the new minimum interval value
2024-05-10 15:11:20 +02:00
30 changed files with 804 additions and 604 deletions

View File

@@ -128,6 +128,8 @@ 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,7 +469,6 @@ 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,6 +177,8 @@ 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"
@@ -592,15 +594,7 @@ 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"
@@ -820,12 +814,20 @@ 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 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.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="{{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="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,7 +386,6 @@ 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,6 +446,7 @@ 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,7 +315,6 @@ 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,6 +434,7 @@ 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="连接的显示器的名称匹配"
@@ -523,7 +524,6 @@ 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(50); duration = std::chrono::milliseconds(10);
} }
} }

View File

@@ -12,6 +12,7 @@ class MacroActionEdit;
class MacroConditionEdit; class MacroConditionEdit;
class Duration; class Duration;
class SequenceWidget; class SequenceWidget;
enum class LogicType;
struct SceneGroup; struct SceneGroup;
/******************************************************************************* /*******************************************************************************
@@ -152,12 +153,16 @@ 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);
@@ -166,16 +171,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);
@@ -184,9 +189,6 @@ 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,8 +29,7 @@ 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(GetGlobalMacroProperties()._highlightActions, : MacroSegmentEdit(parent),
parent),
_actionSelection(new FilterComboBox()), _actionSelection(new FilterComboBox()),
_enable(new SwitchButton()), _enable(new SwitchButton()),
_entryData(entryData) _entryData(entryData)
@@ -40,8 +39,6 @@ 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()));
@@ -161,33 +158,20 @@ std::shared_ptr<MacroSegment> MacroActionEdit::Data() const
return *_entryData; 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()) { if (idx < 0 || idx > (int)macro->Actions().size()) {
assert(false);
return; 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().emplace(macro->Actions().begin() + idx,
macro->Actions().begin() + idx, MacroActionFactory::Create(id, macro));
MacroActionFactory::Create(id, macro.get())); if (data) {
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();
@@ -198,9 +182,37 @@ void AdvSceneSwitcher::AddMacroAction(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();
@@ -216,7 +228,6 @@ 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)
@@ -311,7 +322,6 @@ 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()
@@ -450,31 +460,21 @@ void AdvSceneSwitcher::MacroElseActionReorder(int to, int from)
emit(MacroSegmentOrderChanged()); 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()) { if (idx < 0 || idx > (int)macro->ElseActions().size()) {
assert(false);
return; 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().emplace(macro->ElseActions().begin() + idx,
macro->ElseActions().begin() + idx, MacroActionFactory::Create(id,
MacroActionFactory::Create(id, macro.get())); macro));
if (idx - 1 >= 0) { if (data) {
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,9 +486,37 @@ void AdvSceneSwitcher::AddMacroElseAction(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();
@@ -507,7 +535,7 @@ void AdvSceneSwitcher::RemoveMacroElseAction(int idx)
SetMacroAbortWait(true); SetMacroAbortWait(true);
GetMacroWaitCV().notify_all(); GetMacroWaitCV().notify_all();
macro->UpdateElseActionIndices(); macro->UpdateElseActionIndices();
SetActionData(*macro); SetElseActionData(*macro);
} }
MacroElseActionSelectionChanged(-1); MacroElseActionSelectionChanged(-1);
lastInteracted = MacroSection::ELSE_ACTIONS; lastInteracted = MacroSection::ELSE_ACTIONS;

View File

@@ -48,9 +48,7 @@ bool MacroActionMacro::PerformAction()
macro->ResetRunCount(); macro->ResetRunCount();
break; break;
case Action::RUN: case Action::RUN:
if (!macro->Paused()) { RunActions(macro.get());
macro->PerformActions(true, false, true);
}
break; break;
case Action::STOP: case Action::STOP:
macro->Stop(); macro->Stop();
@@ -127,6 +125,7 @@ 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;
} }
@@ -137,6 +136,15 @@ 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;
} }
@@ -160,11 +168,55 @@ void MacroActionMacro::ResolveVariablesToFixedValues()
_actionIndex.ResolveVariables(); _actionIndex.ResolveVariables();
} }
static inline void populateActionSelection(QComboBox *list) void MacroActionMacro::RunActions(Macro *actionMacro) const
{ {
for (auto entry : actionTypes) { if (_runOptions.skipWhenPaused && actionMacro->Paused()) {
list->addItem(obs_module_text(entry.second.c_str())); 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( MacroActionMacroEdit::MacroActionMacroEdit(
@@ -173,9 +225,20 @@ 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 &)));
@@ -184,15 +247,21 @@ 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 mainLayout = new QHBoxLayout; auto layout = new QVBoxLayout();
PlaceWidgets(obs_module_text("AdvSceneSwitcher.action.macro.entry"), layout->addLayout(_entryLayout);
mainLayout, layout->addLayout(_conditionLayout);
{{"{{actions}}", _actions}, layout->addWidget(_skipWhenPaused);
{"{{actionIndex}}", _actionIndex}, setLayout(layout);
{"{{macros}}", _macros}});
setLayout(mainLayout);
_entryData = entryData; _entryData = entryData;
UpdateEntryData(); UpdateEntryData();
_loading = false; _loading = false;
@@ -207,6 +276,12 @@ 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();
} }
@@ -244,8 +319,88 @@ 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();
@@ -260,6 +415,42 @@ 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,6 +3,7 @@
#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 {
@@ -14,12 +15,27 @@ 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,
@@ -32,8 +48,11 @@ 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;
}; };
@@ -58,18 +77,28 @@ 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,8 +121,7 @@ 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(GetGlobalMacroProperties()._highlightConditions, : MacroSegmentEdit(parent),
parent),
_logicSelection(new QComboBox()), _logicSelection(new QComboBox()),
_conditionSelection(new FilterComboBox()), _conditionSelection(new FilterComboBox()),
_dur(new DurationModifierEdit()), _dur(new DurationModifierEdit()),
@@ -139,8 +138,6 @@ 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);
@@ -299,6 +296,7 @@ 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;
} }
@@ -315,16 +313,31 @@ 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.get())); MacroConditionFactory::Create(id, macro));
if (idx - 1 >= 0) { if (data) {
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();
@@ -337,6 +350,7 @@ void AdvSceneSwitcher::AddMacroCondition(int idx)
SetConditionData(*macro); SetConditionData(*macro);
} }
HighlightCondition(idx); HighlightCondition(idx);
ui->conditionsList->SetHelpMsgVisible(false);
emit(MacroSegmentOrderChanged()); emit(MacroSegmentOrderChanged());
} }
@@ -355,7 +369,6 @@ 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

@@ -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

View File

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

View File

@@ -176,7 +176,8 @@ 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::SetHighlight() void MacroSegment::EnableHighlight()
{ {
_highlight = true; _highlight = true;
} }
bool MacroSegment::Highlight() bool MacroSegment::GetHighlightAndReset()
{ {
if (_highlight) { if (_highlight) {
_highlight = false; _highlight = false;
@@ -194,7 +194,7 @@ void DecrementVariableRef(MacroSegment *segment)
segment->_variableRefs--; segment->_variableRefs--;
} }
MacroSegmentEdit::MacroSegmentEdit(bool highlight, QWidget *parent) MacroSegmentEdit::MacroSegmentEdit(QWidget *parent)
: QWidget(parent), : QWidget(parent),
_section(new Section(300)), _section(new Section(300)),
_headerInfo(new QLabel()), _headerInfo(new QLabel()),
@@ -203,8 +203,7 @@ MacroSegmentEdit::MacroSegmentEdit(bool highlight, 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);
@@ -282,10 +281,6 @@ MacroSegmentEdit::MacroSegmentEdit(bool highlight, 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)
@@ -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() 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 SetHighlight(); void EnableHighlight();
bool Highlight(); bool GetHighlightAndReset();
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(bool highlight, QWidget *parent = nullptr); MacroSegmentEdit(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,8 +100,6 @@ 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);
@@ -143,9 +141,6 @@ private:
QFrame *_dropLineAbove; QFrame *_dropLineAbove;
QFrame *_dropLineBelow; QFrame *_dropLineBelow;
bool _showHighlight;
QTimer _timer;
friend class MacroSegmentList; friend class MacroSegmentList;
}; };

View File

@@ -4,6 +4,7 @@
#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"
@@ -767,8 +768,6 @@ 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,
@@ -783,7 +782,7 @@ static void moveControlsToSplitter(QSplitter *splitter, int idx,
splitter->setStyleSheet("QSplitter::handle {background: transparent;}"); splitter->setStyleSheet("QSplitter::handle {background: transparent;}");
} }
bool shouldRestoreSplitter(const QList<int> &pos) static bool shouldRestoreSplitter(const QList<int> &pos)
{ {
if (pos.size() == 0) { if (pos.size() == 0) {
return false; return false;
@@ -797,6 +796,36 @@ 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);
@@ -891,6 +920,14 @@ 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)
@@ -978,16 +1015,19 @@ 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"));
@@ -999,6 +1039,21 @@ 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));
} }
@@ -1343,58 +1398,47 @@ static void fade(QWidget *widget, bool fadeOut)
animation->start(QPropertyAnimation::DeleteWhenStopped); animation->start(QPropertyAnimation::DeleteWhenStopped);
} }
void AdvSceneSwitcher::FadeOutActionControls() void fadeWidgets(const std::vector<QWidget *> &widgets, bool fadeOut)
{ {
fade(ui->actionAdd, true); for (const auto &widget : widgets) {
fade(ui->actionRemove, true); fade(widget, fadeOut);
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()
{ {
if ((currentActionIdx == -1 && currentConditionIdx == -1) || const std::vector<QWidget *> conditionControls{
(currentActionIdx != -1 && currentConditionIdx != -1)) { ui->conditionAdd, ui->conditionRemove, ui->conditionTop,
ResetOpacityActionControls(); ui->conditionUp, ui->conditionDown, ui->conditionBottom,
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) {
FadeOutConditionControls(); fadeWidgets(conditionControls, true);
ResetOpacityActionControls(); fadeWidgets(actionControls, false);
fadeWidgets(elseActionControls, true);
} else if (currentElseActionIdx != -1) {
fadeWidgets(conditionControls, true);
fadeWidgets(actionControls, true);
fadeWidgets(elseActionControls, false);
} else { } else {
FadeOutActionControls(); assert(false);
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->SetHighlight(); c->EnableHighlight();
} }
break; break;
case LogicType::OR: case LogicType::OR:
_matched = _matched || cond; _matched = _matched || cond;
if (cond) { if (cond) {
c->SetHighlight(); c->EnableHighlight();
} }
break; break;
case LogicType::AND_NOT: case LogicType::AND_NOT:
_matched = _matched && !cond; _matched = _matched && !cond;
if (!cond) { if (!cond) {
c->SetHighlight(); c->EnableHighlight();
} }
break; break;
case LogicType::OR_NOT: case LogicType::OR_NOT:
_matched = _matched || !cond; _matched = _matched || !cond;
if (!cond) { if (!cond) {
c->SetHighlight(); c->EnableHighlight();
} }
break; break;
case LogicType::ROOT_NONE: case LogicType::ROOT_NONE:
_matched = cond; _matched = cond;
if (cond) { if (cond) {
c->SetHighlight(); c->EnableHighlight();
} }
break; break;
case LogicType::ROOT_NOT: case LogicType::ROOT_NOT:
_matched = !cond; _matched = !cond;
if (!cond) { if (!cond) {
c->SetHighlight(); c->EnableHighlight();
} }
break; break;
default: default:
@@ -313,7 +313,7 @@ bool Macro::RunActionsHelper(
break; break;
} }
if (action->Enabled()) { if (action->Enabled()) {
action->SetHighlight(); action->EnableHighlight();
} }
} }
_done = true; _done = true;
@@ -819,10 +819,10 @@ void Macro::ResetUIHelpers()
{ {
_onPreventedActionExecution = false; _onPreventedActionExecution = false;
for (auto c : _conditions) { for (auto c : _conditions) {
c->Highlight(); c->GetHighlightAndReset();
} }
for (auto a : _actions) { for (auto a : _actions) {
a->Highlight(); a->GetHighlightAndReset();
} }
} }

View File

@@ -67,7 +67,21 @@ bool MacroConditionAudio::CheckOutputCondition()
OBSSourceAutoRelease source = OBSSourceAutoRelease source =
obs_weak_source_get_source(_audioSource.GetSource()); obs_weak_source_get_source(_audioSource.GetSource());
double curVolume = _useDb ? _peak : DecibelToPercent(_peak) * 100; // OBS might very 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.
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:
@@ -92,7 +106,6 @@ 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();
} }
@@ -329,11 +342,13 @@ 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,7 +73,10 @@ 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,11 +45,13 @@ static bool setupReplayBufferEventHandler()
bool MacroConditionReplayBuffer::ReplayBufferWasSaved() bool MacroConditionReplayBuffer::ReplayBufferWasSaved()
{ {
bool timersAreInitialized = if (!_saveTimeInitialized) {
_saveTime.time_since_epoch().count() != 0 && _saveTime = replayBufferSaveTime;
replayBufferSaveTime.time_since_epoch().count() != 0; _saveTimeInitialized = true;
bool newSaveOccurred = timersAreInitialized && return false;
_saveTime != replayBufferSaveTime; }
bool newSaveOccurred = _saveTime != replayBufferSaveTime;
_saveTime = replayBufferSaveTime; _saveTime = replayBufferSaveTime;
return newSaveOccurred; return newSaveOccurred;
} }

View File

@@ -29,6 +29,7 @@ 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,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]; wchar_t wTitle[512];
os_utf8_to_wcs(title.c_str(), 0, wTitle, 512); os_utf8_to_wcs(title.c_str(), 0, wTitle, 512);
hwnd = FindWindowEx(NULL, NULL, NULL, wTitle); while ((hwnd = FindWindowEx(NULL, hPrevWnd, NULL, wTitle)) != nullptr) {
return hwnd; if (windowIsValid(hwnd)) {
hwnds.emplace_back(hwnd);
}
hPrevWnd = hwnd;
}
return hwnds;
} }
std::string GetWindowClassByWindowTitle(const std::string &window) std::string GetWindowClassByWindowTitle(const std::string &window)
{ {
HWND hwnd = NULL; auto hwnds = getHWNDfromTitle(window);
hwnd = getHWNDfromTitle(window); if (hwnds.empty()) {
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())) {
@@ -206,41 +227,54 @@ std::string GetWindowClassByWindowTitle(const std::string &window)
void SetFocusWindow(const std::string &title) void SetFocusWindow(const std::string &title)
{ {
auto handle = getHWNDfromTitle(title); auto hwnds = getHWNDfromTitle(title);
if (!handle) { if (hwnds.empty()) {
return; return;
} }
ShowWindow(handle, SW_RESTORE);
SetForegroundWindow(handle); for (const auto &handle : hwnds) {
SetFocus(handle); const bool isMinimized = IsIconic(handle);
SetActiveWindow(handle); ShowWindow(handle, isMinimized ? SW_RESTORE : SW_SHOW);
SetForegroundWindow(handle);
SetFocus(handle);
SetActiveWindow(handle);
}
} }
void CloseWindow(const std::string &title) void CloseWindow(const std::string &title)
{ {
auto handle = getHWNDfromTitle(title); auto hwnds = getHWNDfromTitle(title);
if (!handle) { if (hwnds.empty()) {
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 handle = getHWNDfromTitle(title); auto hwnds = getHWNDfromTitle(title);
if (!handle) { if (hwnds.empty()) {
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 handle = getHWNDfromTitle(title); auto hwnds = getHWNDfromTitle(title);
if (!handle) { if (hwnds.empty()) {
return; return;
} }
ShowWindow(handle, SW_MINIMIZE);
for (const auto &handle : hwnds) {
ShowWindow(handle, SW_MINIMIZE);
}
} }
class RawMouseInputFilter; class RawMouseInputFilter;

View File

@@ -12,73 +12,7 @@ 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;
@@ -106,27 +40,37 @@ bool MacroConditionMidi::CheckMessage()
return false; return false;
} }
bool MacroConditionMidi::CheckConnectedDevcieNames() bool MacroConditionMidi::Save(obs_data_t *obj) const
{ {
auto deviceNames = GetDeviceNames(); MacroCondition::Save(obj);
if (!_regex.Enabled()) { _message.Save(obj);
return std::find(deviceNames.begin(), deviceNames.end(), _device.Save(obj);
std::string(_deviceName)) != deviceNames.end(); return true;
} }
for (const auto &deviceName : deviceNames) {
if (_regex.Matches(deviceName, _deviceName)) { bool MacroConditionMidi::Load(obs_data_t *obj)
return true; {
} MacroCondition::Load(obj);
} _message.Load(obj);
return false; _device.Load(obj);
_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",
@@ -154,45 +98,16 @@ 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,
@@ -206,21 +121,19 @@ 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);
@@ -242,14 +155,11 @@ 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(
_entryData->_deviceName.UnresolvedValue())); adjustSize();
_regex->SetRegexConfig(_entryData->_regex); updateGeometry();
SetWidgetVisibility();
} }
void MacroConditionMidiEdit::DeviceSelectionChanged(const MidiDevice &device) void MacroConditionMidiEdit::DeviceSelectionChanged(const MidiDevice &device)
@@ -301,46 +211,6 @@ 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) {
@@ -379,41 +249,4 @@ 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,8 +1,6 @@
#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>
@@ -25,23 +23,11 @@ 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{};
@@ -72,29 +58,18 @@ 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,47 +20,56 @@ 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;
for (auto api : libremidi::available_apis()) { try {
libremidi::observer_configuration cbs; for (auto api : libremidi::available_apis()) {
cbs.track_virtual = true; libremidi::observer_configuration cbs;
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 = [](const libremidi::output_port &p) { cbs.output_removed =
auto dev = MidiDeviceInstance::GetDevice(p); [=](const libremidi::output_port &p) {
if (!dev) { auto dev =
return; MidiDeviceInstance::GetDevice(
} p);
blog(LOG_INFO, "MIDI output removed: %s", if (!dev) {
p.port_name.c_str()); return;
}; }
observers.emplace_back( blog(LOG_INFO,
cbs, libremidi::observer_configuration_for(api)); "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; return true;
} }
@@ -329,95 +338,35 @@ advss::MidiDeviceInstance::GetDevice(const libremidi::output_port &p)
static inline QStringList getInputDeviceNames() static inline QStringList getInputDeviceNames()
{ {
static QStringList devices; 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 {
for (auto api : libremidi::available_apis()) { libremidi::observer obs;
libremidi::observer_configuration cbs; for (const libremidi::input_port &port :
cbs.track_virtual = true; obs.get_input_ports()) {
cbs.input_added = [](const libremidi::input_port &p) { devices << QString::fromStdString(
std::lock_guard<std::mutex> lock(m); getNameFromPortInformation(port));
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()
{ {
static QStringList devices; 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 {
for (auto api : libremidi::available_apis()) { libremidi::observer obs;
libremidi::observer_configuration cbs; for (const libremidi::output_port &port :
cbs.track_virtual = true; obs.get_output_ports()) {
cbs.output_added = [](const libremidi::output_port &p) { devices << QString::fromStdString(
std::lock_guard<std::mutex> lock(m); getNameFromPortInformation(port));
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;
} }
@@ -485,11 +434,16 @@ 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)
{ {
libremidi::observer obs; try {
for (const libremidi::output_port &port : obs.get_output_ports()) { libremidi::observer obs;
if (getNameFromPortInformation(port) == name) { for (const libremidi::output_port &port :
return 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 {}; return {};
} }
@@ -497,11 +451,16 @@ 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)
{ {
libremidi::observer obs; try {
for (const libremidi::input_port &port : obs.get_input_ports()) { libremidi::observer obs;
if (getNameFromPortInformation(port) == name) { for (const libremidi::input_port &port :
return 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 {}; return {};
} }
@@ -1058,28 +1017,6 @@ 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,8 +171,6 @@ 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,6 +29,12 @@ 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();
@@ -211,11 +217,11 @@ static void setupTab(QTabWidget *tab)
} }
}); });
auto timer = new QTimer(tabWidget); QWidget::connect(tab, &QTabWidget::currentChanged, [tab]() {
timer->setInterval(1000); if (twitchTabIsSelected(tab)) {
QWidget::connect(timer, &QTimer::timeout, updateConnectionStatus(tabWidget->Table());
[]() { updateConnectionStatus(tabWidget->Table()); }); }
timer->start(); });
} }
} // namespace advss } // namespace advss