mirror of
https://github.com/WarmUpTill/SceneSwitcher.git
synced 2026-08-30 21:25:32 -05:00
Compare commits
19 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5dfed8ac2a | ||
|
|
d7102d395e | ||
|
|
82a41694a1 | ||
|
|
a1cae263c1 | ||
|
|
cce6e240c2 | ||
|
|
9d04b4d1a0 | ||
|
|
fd7e48eff9 | ||
|
|
0350a46004 | ||
|
|
00fec4b573 | ||
|
|
c40a3af8ca | ||
|
|
e452d8cc8c | ||
|
|
fbc9818764 | ||
|
|
5ef09f661f | ||
|
|
2155a942f3 | ||
|
|
5af206da60 | ||
|
|
924f68df64 | ||
|
|
2f3c4a6dae | ||
|
|
f955c07bda | ||
|
|
6f1cd3cbcc |
2
.github/workflows/build.yml
vendored
2
.github/workflows/build.yml
vendored
@@ -5,7 +5,7 @@ on: [push, pull_request, workflow_dispatch]
|
||||
env:
|
||||
PLUGIN_NAME: SceneSwitcher
|
||||
LIB_NAME: advanced-scene-switcher
|
||||
OBS_TAG: 27.1.1
|
||||
OBS_TAG: 27.1.3
|
||||
|
||||
jobs:
|
||||
macos64:
|
||||
|
||||
@@ -147,7 +147,6 @@ if(UNIX AND NOT APPLE)
|
||||
if(NOT PROCPS_LIBRARY)
|
||||
message(FATAL_ERROR "procps lib not found - please set PROCPS_LIBRARY")
|
||||
endif()
|
||||
message (STATUS "WHAT THE FUCK IS GOING ON ${PROCPS_LIBRARY}")
|
||||
link_libraries(${X11_LIBRARIES} ${procps_LIBRARIES})
|
||||
include_directories("${X11_INCLUDE_DIR}" "${X11_Xtst_INCLUDE_PATH}"
|
||||
"${X11_Xss_INCLUDE_PATH}" "${PROCPS_INCLUDE_DIR}")
|
||||
@@ -176,10 +175,6 @@ if(WIN32)
|
||||
add_definitions(-D_WEBSOCKETPP_CPP11_STL_)
|
||||
endif()
|
||||
|
||||
# opencv
|
||||
find_package(OpenCV REQUIRED)
|
||||
include_directories("${OpenCV_INCLUDE_DIRS}")
|
||||
|
||||
# Setup QT tools
|
||||
set(CMAKE_AUTOMOC ON)
|
||||
set(CMAKE_AUTOUIC ON)
|
||||
@@ -220,6 +215,7 @@ set(advanced-scene-switcher_HEADERS
|
||||
src/headers/macro-action-media.hpp
|
||||
src/headers/macro-action-plugin-state.hpp
|
||||
src/headers/macro-action-preview-scene.hpp
|
||||
src/headers/macro-action-random.hpp
|
||||
src/headers/macro-action-recording.hpp
|
||||
src/headers/macro-action-replay-buffer.hpp
|
||||
src/headers/macro-action-run.hpp
|
||||
@@ -250,6 +246,7 @@ set(advanced-scene-switcher_HEADERS
|
||||
src/headers/macro-condition-replay-buffer.hpp
|
||||
src/headers/macro-condition-scene-order.hpp
|
||||
src/headers/macro-condition-scene-transform.hpp
|
||||
src/headers/macro-condition-scene-visibility.hpp
|
||||
src/headers/macro-condition-scene.hpp
|
||||
src/headers/macro-condition-source.hpp
|
||||
src/headers/macro-condition-streaming.hpp
|
||||
@@ -311,6 +308,7 @@ set(advanced-scene-switcher_SOURCES
|
||||
src/macro-action-media.cpp
|
||||
src/macro-action-plugin-state.cpp
|
||||
src/macro-action-preview-scene.cpp
|
||||
src/macro-action-random.cpp
|
||||
src/macro-action-recording.cpp
|
||||
src/macro-action-replay-buffer.cpp
|
||||
src/macro-action-run.cpp
|
||||
@@ -341,6 +339,7 @@ set(advanced-scene-switcher_SOURCES
|
||||
src/macro-condition-replay-buffer.cpp
|
||||
src/macro-condition-scene-order.cpp
|
||||
src/macro-condition-scene-transform.cpp
|
||||
src/macro-condition-scene-visibility.cpp
|
||||
src/macro-condition-scene.cpp
|
||||
src/macro-condition-source.cpp
|
||||
src/macro-condition-streaming.cpp
|
||||
@@ -365,6 +364,22 @@ set(advanced-scene-switcher_SOURCES
|
||||
src/volume-control.cpp
|
||||
src/version.cpp)
|
||||
|
||||
# opencv
|
||||
find_package(OpenCV)
|
||||
if(OpenCV_FOUND)
|
||||
include_directories("${OpenCV_INCLUDE_DIRS}")
|
||||
else()
|
||||
set(OpenCV_LIBRARIES "")
|
||||
message(
|
||||
WARNING
|
||||
"OpenCV not found! Functionality relying on OpenCV will be disabled!\nOpenCV sources are available under: ${CMAKE_CURRENT_SOURCE_DIR}/deps/opencv"
|
||||
)
|
||||
list(REMOVE_ITEM advanced-scene-switcher_SOURCES
|
||||
src/macro-condition-video.cpp)
|
||||
list(REMOVE_ITEM advanced-scene-switcher_HEADERS
|
||||
src/headers/macro-condition-video.hpp)
|
||||
endif()
|
||||
|
||||
add_library(
|
||||
advanced-scene-switcher MODULE
|
||||
${advanced-scene-switcher_HEADERS}
|
||||
|
||||
@@ -45,7 +45,7 @@ AdvSceneSwitcher.generalTab.priority.fileContent="Datei Inhalt"
|
||||
AdvSceneSwitcher.generalTab.priority.sceneSequence="Szenenfolge"
|
||||
AdvSceneSwitcher.generalTab.priority.idleDetection="Inaktivitätserkennung"
|
||||
AdvSceneSwitcher.generalTab.priority.executable="Prozess"
|
||||
AdvSceneSwitcher.generalTab.priority.screenRegion="Bildschrimbereich"
|
||||
AdvSceneSwitcher.generalTab.priority.screenRegion="Bildschirmbereich"
|
||||
AdvSceneSwitcher.generalTab.priority.windowTitle="Fenstername"
|
||||
AdvSceneSwitcher.generalTab.priority.media="Medien"
|
||||
AdvSceneSwitcher.generalTab.priority.time="Zeit"
|
||||
@@ -98,7 +98,7 @@ AdvSceneSwitcher.executableTab.entry="Wenn {{processes}} läuft wechsle zu {{sce
|
||||
AdvSceneSwitcher.executableTab.help="Dieser Tab erlaubt es Szenen zu wechseln, wenn bestimmte Prozesse laufen.\nDas kann nützlich sein, wenn sich der Name eines Fensters ändern kann oder dieser nicht bekannt ist.\n\nKlicke auf das markierte Plus Symbol, um einen neuen Eintrag hinzuzufügen."
|
||||
|
||||
; Screen Region Tab
|
||||
AdvSceneSwitcher.screenRegionTab.title="Bildschrimbereich"
|
||||
AdvSceneSwitcher.screenRegionTab.title="Bildschirmbereich"
|
||||
AdvSceneSwitcher.screenRegionTab.currentPosition="Mauszeiger ist bei:"
|
||||
AdvSceneSwitcher.screenRegionTab.showGuideFrames="Hilfsrahmen einblenden"
|
||||
AdvSceneSwitcher.screenRegionTab.hideGuideFrames="Hilfsrahmen ausblenden"
|
||||
|
||||
@@ -96,7 +96,7 @@ AdvSceneSwitcher.condition.cursor.entry.line2="Cursor is currently at {{xPos}} x
|
||||
AdvSceneSwitcher.condition.scene="Scene"
|
||||
AdvSceneSwitcher.condition.scene.type.current="Current scene is"
|
||||
AdvSceneSwitcher.condition.scene.type.previous="Previous scene is"
|
||||
AdvSceneSwitcher.condition.scene.type.changed="Scene chagned"
|
||||
AdvSceneSwitcher.condition.scene.type.changed="Scene changed"
|
||||
AdvSceneSwitcher.condition.scene.type.notChanged="Scene has not changed"
|
||||
AdvSceneSwitcher.condition.scene.waitForTransition="Wait for transition to complete"
|
||||
AdvSceneSwitcher.condition.scene.entry="{{sceneType}} {{scenes}} {{waitForTransition}}"
|
||||
@@ -172,6 +172,7 @@ AdvSceneSwitcher.condition.macro.count.type.below="Less than"
|
||||
AdvSceneSwitcher.condition.macro.count.type.above="More than"
|
||||
AdvSceneSwitcher.condition.macro.count.type.equal="Exactly"
|
||||
AdvSceneSwitcher.condition.macro.count.reset="Reset"
|
||||
AdvSceneSwitcher.condition.macro.pausedWarning="Selected macro is currently paused!"
|
||||
AdvSceneSwitcher.condition.macro.state.entry="Conditions of {{macros}} are true"
|
||||
AdvSceneSwitcher.condition.macro.count.entry.line1="{{macros}} was executed {{conditions}} {{count}} times"
|
||||
AdvSceneSwitcher.condition.macro.count.entry.line2="Current count: {{currentCount}} {{resetCount}}"
|
||||
@@ -232,6 +233,10 @@ AdvSceneSwitcher.condition.transition.type.started="Transition active"
|
||||
AdvSceneSwitcher.condition.transition.type.ended="Transition ended"
|
||||
AdvSceneSwitcher.condition.transition.durationSuffix="seconds"
|
||||
AdvSceneSwitcher.condition.transition.entry="{{conditions}}{{transitions}}{{duration}}{{durationSuffix}}"
|
||||
AdvSceneSwitcher.condition.sceneVisibility="Scene item visibility"
|
||||
AdvSceneSwitcher.condition.sceneVisibility.type.shown="Shown"
|
||||
AdvSceneSwitcher.condition.sceneVisibility.type.hidden="Hidden"
|
||||
AdvSceneSwitcher.condition.sceneVisibility.entry="On {{scenes}} {{sources}} is {{conditions}} "
|
||||
|
||||
; Macro Actions
|
||||
AdvSceneSwitcher.action.switchScene="Switch scene"
|
||||
@@ -246,6 +251,7 @@ AdvSceneSwitcher.action.audio.type.mute="Mute"
|
||||
AdvSceneSwitcher.action.audio.type.unmute="Unmute"
|
||||
AdvSceneSwitcher.action.audio.type.sourceVolume="Set source volume"
|
||||
AdvSceneSwitcher.action.audio.type.masterVolume="Set master volume"
|
||||
AdvSceneSwitcher.action.audio.fade="{{fade}}Fade over {{duration}} seconds"
|
||||
AdvSceneSwitcher.action.audio.entry="{{actions}} {{audioSources}} {{volume}}"
|
||||
AdvSceneSwitcher.action.recording="Recording"
|
||||
AdvSceneSwitcher.action.recording.type.stop="Stop recording"
|
||||
@@ -271,7 +277,9 @@ AdvSceneSwitcher.action.run.entry="Run {{filePath}}"
|
||||
AdvSceneSwitcher.action.sceneVisibility="Scene item visibility"
|
||||
AdvSceneSwitcher.action.sceneVisibility.type.show="Show"
|
||||
AdvSceneSwitcher.action.sceneVisibility.type.hide="Hide"
|
||||
AdvSceneSwitcher.action.sceneVisibility.entry="On {{scenes}} {{actions}} {{sources}}"
|
||||
AdvSceneSwitcher.action.sceneVisibility.type.source="Source"
|
||||
AdvSceneSwitcher.action.sceneVisibility.type.sourceGroup="Any"
|
||||
AdvSceneSwitcher.action.sceneVisibility.entry="On {{scenes}} {{actions}} {{sourceTypes}} {{sources}}"
|
||||
AdvSceneSwitcher.action.filter="Filter"
|
||||
AdvSceneSwitcher.action.filter.type.enable="Enable"
|
||||
AdvSceneSwitcher.action.filter.type.disable="Disable"
|
||||
@@ -345,6 +353,10 @@ AdvSceneSwitcher.action.timer.type.continue="Continue"
|
||||
AdvSceneSwitcher.action.timer.type.reset="Reset"
|
||||
AdvSceneSwitcher.action.timer.type.setTimeRemaining="Set time remaining of"
|
||||
AdvSceneSwitcher.action.timer.entry="{{timerAction}} timers on {{macros}} {{duration}}"
|
||||
AdvSceneSwitcher.action.random="Random"
|
||||
AdvSceneSwitcher.action.random.arguments="Macros:"
|
||||
AdvSceneSwitcher.action.random.addArgument="Add Macro"
|
||||
AdvSceneSwitcher.action.random.entry="Run {{macroSelection}}"
|
||||
|
||||
; Transition Tab
|
||||
AdvSceneSwitcher.transitionTab.title="Transition"
|
||||
|
||||
@@ -20,6 +20,7 @@ SwitcherData *switcher = nullptr;
|
||||
AdvSceneSwitcher::AdvSceneSwitcher(QWidget *parent)
|
||||
: QDialog(parent), ui(new Ui_AdvSceneSwitcher)
|
||||
{
|
||||
switcher->settingsWindowOpened = true;
|
||||
ui->setupUi(this);
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
@@ -28,6 +29,13 @@ AdvSceneSwitcher::AdvSceneSwitcher(QWidget *parent)
|
||||
loadUI();
|
||||
}
|
||||
|
||||
AdvSceneSwitcher::~AdvSceneSwitcher()
|
||||
{
|
||||
if (switcher) {
|
||||
switcher->settingsWindowOpened = false;
|
||||
}
|
||||
}
|
||||
|
||||
bool translationAvailable()
|
||||
{
|
||||
return !!strcmp(obs_module_text("AdvSceneSwitcher.pluginName"),
|
||||
@@ -422,7 +430,7 @@ void SwitcherData::Start()
|
||||
void ResetMacroCounters()
|
||||
{
|
||||
for (auto &m : switcher->macros) {
|
||||
m.ResetCount();
|
||||
m->ResetCount();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -444,6 +452,13 @@ void SwitcherData::Stop()
|
||||
server.stop();
|
||||
client.disconnect();
|
||||
|
||||
for (auto &t : audioHelperThreads) {
|
||||
if (t.joinable()) {
|
||||
t.join();
|
||||
}
|
||||
}
|
||||
audioHelperThreads.clear();
|
||||
|
||||
if (showSystemTrayNotifications) {
|
||||
DisplayTrayMessage(
|
||||
obs_module_text("AdvSceneSwitcher.pluginName"),
|
||||
@@ -628,6 +643,8 @@ static void OBSEvent(enum obs_frontend_event event, void *switcher)
|
||||
}
|
||||
}
|
||||
|
||||
AdvSceneSwitcher *ssWindow;
|
||||
|
||||
extern "C" void InitSceneSwitcher()
|
||||
{
|
||||
blog(LOG_INFO, "version: %s", g_GIT_TAG);
|
||||
@@ -645,11 +662,16 @@ extern "C" void InitSceneSwitcher()
|
||||
PlatformInit();
|
||||
|
||||
auto cb = []() {
|
||||
QMainWindow *window =
|
||||
(QMainWindow *)obs_frontend_get_main_window();
|
||||
|
||||
AdvSceneSwitcher ss(window);
|
||||
ss.exec();
|
||||
if (switcher->settingsWindowOpened) {
|
||||
ssWindow->show();
|
||||
ssWindow->raise();
|
||||
ssWindow->activateWindow();
|
||||
} else {
|
||||
ssWindow = new AdvSceneSwitcher(
|
||||
(QMainWindow *)obs_frontend_get_main_window());
|
||||
ssWindow->setAttribute(Qt::WA_DeleteOnClose);
|
||||
ssWindow->show();
|
||||
}
|
||||
};
|
||||
|
||||
obs_frontend_add_save_callback(SaveSceneSwitcher, nullptr);
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
#include <QFileDialog>
|
||||
|
||||
FileSelection::FileSelection(FileSelection::Type type, QWidget *parent)
|
||||
: _type(type), QWidget(parent)
|
||||
: QWidget(parent), _type(type)
|
||||
{
|
||||
_filePath = new QLineEdit();
|
||||
_browseButton =
|
||||
|
||||
@@ -186,6 +186,9 @@ void AdvSceneSwitcher::on_toggleStartButton_clicked()
|
||||
|
||||
void AdvSceneSwitcher::closeEvent(QCloseEvent *)
|
||||
{
|
||||
if (!switcher) {
|
||||
return;
|
||||
}
|
||||
switcher->windowPos = this->pos();
|
||||
switcher->windowSize = this->size();
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@ public:
|
||||
bool currentStatusActive = false;
|
||||
|
||||
AdvSceneSwitcher(QWidget *parent);
|
||||
~AdvSceneSwitcher();
|
||||
|
||||
void reject() override;
|
||||
void closeEvent(QCloseEvent *event) override;
|
||||
@@ -52,8 +53,8 @@ public:
|
||||
void SetEditMacro(Macro &m);
|
||||
void HighlightAction(int idx);
|
||||
void HighlightCondition(int idx);
|
||||
void PopulateMacroActions(Macro &m, int afterIdx = 0);
|
||||
void PopulateMacroConditions(Macro &m, int afterIdx = 0);
|
||||
void PopulateMacroActions(Macro &m, uint32_t afterIdx = 0);
|
||||
void PopulateMacroConditions(Macro &m, uint32_t afterIdx = 0);
|
||||
void ConnectControlSignals(MacroActionEdit *);
|
||||
void ConnectControlSignals(MacroConditionEdit *);
|
||||
void SwapActions(Macro *m, int pos1, int pos2);
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
#pragma once
|
||||
#include <QSpinBox>
|
||||
#include <QHBoxLayout>
|
||||
#include "macro-action-edit.hpp"
|
||||
#include "duration-control.hpp"
|
||||
|
||||
#include <QSpinBox>
|
||||
#include <QCheckBox>
|
||||
#include <QHBoxLayout>
|
||||
|
||||
enum class AudioAction {
|
||||
MUTE,
|
||||
@@ -26,6 +29,8 @@ public:
|
||||
OBSWeakSource _audioSource;
|
||||
AudioAction _action = AudioAction::MUTE;
|
||||
int _volume = 0;
|
||||
bool _fade = false;
|
||||
Duration _duration;
|
||||
|
||||
private:
|
||||
static bool _registered;
|
||||
@@ -53,6 +58,8 @@ private slots:
|
||||
void SourceChanged(const QString &text);
|
||||
void ActionChanged(int value);
|
||||
void VolumeChanged(int value);
|
||||
void FadeChanged(int value);
|
||||
void DurationChanged(double seconds);
|
||||
signals:
|
||||
void HeaderInfoChanged(const QString &);
|
||||
|
||||
@@ -60,9 +67,11 @@ protected:
|
||||
QComboBox *_audioSources;
|
||||
QComboBox *_actions;
|
||||
QSpinBox *_volumePercent;
|
||||
QCheckBox *_fade;
|
||||
DurationSelection *_duration;
|
||||
QHBoxLayout *_fadeLayout;
|
||||
std::shared_ptr<MacroActionAudio> _entryData;
|
||||
|
||||
private:
|
||||
QHBoxLayout *_mainLayout;
|
||||
bool _loading = true;
|
||||
};
|
||||
|
||||
63
src/headers/macro-action-random.hpp
Normal file
63
src/headers/macro-action-random.hpp
Normal file
@@ -0,0 +1,63 @@
|
||||
#pragma once
|
||||
#include "macro-action-edit.hpp"
|
||||
#include "macro-selection.hpp"
|
||||
|
||||
#include <QPushButton>
|
||||
#include <QListWidget>
|
||||
#include <unordered_map>
|
||||
|
||||
class MacroActionRandom : public MultiMacroRefAction {
|
||||
public:
|
||||
bool PerformAction();
|
||||
void LogAction();
|
||||
bool Save(obs_data_t *obj);
|
||||
bool Load(obs_data_t *obj);
|
||||
std::string GetId() { return id; };
|
||||
static std::shared_ptr<MacroAction> Create()
|
||||
{
|
||||
return std::make_shared<MacroActionRandom>();
|
||||
}
|
||||
|
||||
// TODO: add weights to each macro ...
|
||||
// std::unordered_map<MacroRef, int> _weights;
|
||||
|
||||
private:
|
||||
MacroRef lastRandomMacro;
|
||||
static bool _registered;
|
||||
static const std::string id;
|
||||
};
|
||||
|
||||
class MacroActionRandomEdit : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MacroActionRandomEdit(
|
||||
QWidget *parent,
|
||||
std::shared_ptr<MacroActionRandom> entryData = nullptr);
|
||||
void UpdateEntryData();
|
||||
static QWidget *Create(QWidget *parent,
|
||||
std::shared_ptr<MacroAction> action)
|
||||
{
|
||||
return new MacroActionRandomEdit(
|
||||
parent,
|
||||
std::dynamic_pointer_cast<MacroActionRandom>(action));
|
||||
}
|
||||
|
||||
private slots:
|
||||
void MacroRemove(const QString &name);
|
||||
void MacroRename(const QString &oldName, const QString &newName);
|
||||
void AddMacro();
|
||||
void RemoveMacro();
|
||||
int FindEntry(const std::string ¯o);
|
||||
void MacroSelectionChanged(int idx);
|
||||
|
||||
protected:
|
||||
std::shared_ptr<MacroActionRandom> _entryData;
|
||||
|
||||
private:
|
||||
MacroSelection *_macroSelection;
|
||||
QListWidget *_macroList;
|
||||
QPushButton *_add;
|
||||
QPushButton *_remove;
|
||||
bool _loading = true;
|
||||
};
|
||||
@@ -8,6 +8,11 @@ enum class SceneVisibilityAction {
|
||||
HIDE,
|
||||
};
|
||||
|
||||
enum class SceneItemSourceType {
|
||||
SOURCE,
|
||||
SOURCE_GROUP,
|
||||
};
|
||||
|
||||
class MacroActionSceneVisibility : public MacroAction {
|
||||
public:
|
||||
bool PerformAction();
|
||||
@@ -22,7 +27,9 @@ public:
|
||||
}
|
||||
|
||||
SceneSelection _scene;
|
||||
SceneItemSourceType _sourceType = SceneItemSourceType::SOURCE;
|
||||
OBSWeakSource _source;
|
||||
std::string _sourceGroup = "";
|
||||
SceneVisibilityAction _action = SceneVisibilityAction::SHOW;
|
||||
|
||||
private:
|
||||
@@ -49,6 +56,7 @@ public:
|
||||
|
||||
private slots:
|
||||
void SceneChanged(const SceneSelection &);
|
||||
void SourceTypeChanged(int value);
|
||||
void SourceChanged(const QString &text);
|
||||
void ActionChanged(int value);
|
||||
signals:
|
||||
@@ -56,6 +64,7 @@ signals:
|
||||
|
||||
protected:
|
||||
SceneSelectionWidget *_scenes;
|
||||
QComboBox *_sourceTypes;
|
||||
QComboBox *_sources;
|
||||
QComboBox *_actions;
|
||||
std::shared_ptr<MacroActionSceneVisibility> _entryData;
|
||||
|
||||
@@ -67,6 +67,7 @@ private slots:
|
||||
void ConditionChanged(int cond);
|
||||
void ResetClicked();
|
||||
void UpdateCount();
|
||||
void UpdatePaused();
|
||||
signals:
|
||||
void HeaderInfoChanged(const QString &);
|
||||
|
||||
@@ -76,16 +77,17 @@ protected:
|
||||
QComboBox *_counterConditions;
|
||||
QSpinBox *_count;
|
||||
QLabel *_currentCount;
|
||||
QLabel *_pausedWarning;
|
||||
QPushButton *_resetCount;
|
||||
QHBoxLayout *_settingsLine1;
|
||||
QHBoxLayout *_settingsLine2;
|
||||
std::unique_ptr<QTimer> _timer;
|
||||
QTimer _countTimer;
|
||||
QTimer _pausedTimer;
|
||||
std::shared_ptr<MacroConditionMacro> _entryData;
|
||||
|
||||
private:
|
||||
void ClearLayouts();
|
||||
void SetupStateWidgets();
|
||||
void SetupCountWidgets();
|
||||
void ResetTimer();
|
||||
bool _loading = true;
|
||||
};
|
||||
|
||||
66
src/headers/macro-condition-scene-visibility.hpp
Normal file
66
src/headers/macro-condition-scene-visibility.hpp
Normal file
@@ -0,0 +1,66 @@
|
||||
#pragma once
|
||||
#include "macro.hpp"
|
||||
#include "scene-selection.hpp"
|
||||
|
||||
#include <QComboBox>
|
||||
|
||||
enum class SceneVisibilityCondition {
|
||||
SHOWN,
|
||||
HIDDEN,
|
||||
};
|
||||
|
||||
class MacroConditionSceneVisibility : public MacroCondition {
|
||||
public:
|
||||
bool CheckCondition();
|
||||
bool Save(obs_data_t *obj);
|
||||
bool Load(obs_data_t *obj);
|
||||
std::string GetShortDesc();
|
||||
std::string GetId() { return id; };
|
||||
static std::shared_ptr<MacroCondition> Create()
|
||||
{
|
||||
return std::make_shared<MacroConditionSceneVisibility>();
|
||||
}
|
||||
|
||||
SceneSelection _scene;
|
||||
OBSWeakSource _source;
|
||||
SceneVisibilityCondition _condition = SceneVisibilityCondition::SHOWN;
|
||||
|
||||
private:
|
||||
static bool _registered;
|
||||
static const std::string id;
|
||||
};
|
||||
|
||||
class MacroConditionSceneVisibilityEdit : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MacroConditionSceneVisibilityEdit(
|
||||
QWidget *parent,
|
||||
std::shared_ptr<MacroConditionSceneVisibility> cond = nullptr);
|
||||
void UpdateEntryData();
|
||||
static QWidget *Create(QWidget *parent,
|
||||
std::shared_ptr<MacroCondition> cond)
|
||||
{
|
||||
return new MacroConditionSceneVisibilityEdit(
|
||||
parent,
|
||||
std::dynamic_pointer_cast<MacroConditionSceneVisibility>(
|
||||
cond));
|
||||
}
|
||||
|
||||
private slots:
|
||||
void SceneChanged(const SceneSelection &);
|
||||
void SourceChanged(const QString &text);
|
||||
void ConditionChanged(int cond);
|
||||
signals:
|
||||
void HeaderInfoChanged(const QString &);
|
||||
|
||||
protected:
|
||||
SceneSelectionWidget *_scenes;
|
||||
QComboBox *_sources;
|
||||
QComboBox *_conditions;
|
||||
|
||||
std::shared_ptr<MacroConditionSceneVisibility> _entryData;
|
||||
|
||||
private:
|
||||
bool _loading = true;
|
||||
};
|
||||
@@ -77,7 +77,7 @@ public:
|
||||
bool Matched() { return _matched; }
|
||||
std::string Name() { return _name; }
|
||||
void SetName(const std::string &name);
|
||||
void SetPaused(bool pause = true) { _paused = pause; }
|
||||
void SetPaused(bool pause = true);
|
||||
bool Paused() { return _paused; }
|
||||
int GetCount() { return _count; };
|
||||
void ResetCount() { _count = 0; };
|
||||
@@ -102,6 +102,7 @@ private:
|
||||
void SetupHotkeys();
|
||||
void ClearHotkeys();
|
||||
void SetHotkeysDesc();
|
||||
void ResetTimers();
|
||||
|
||||
std::string _name = "";
|
||||
std::deque<std::shared_ptr<MacroCondition>> _conditions;
|
||||
@@ -144,3 +145,9 @@ public:
|
||||
void ResolveMacroRef();
|
||||
MacroRef _macro;
|
||||
};
|
||||
|
||||
class MultiMacroRefAction : public MacroAction {
|
||||
public:
|
||||
void ResolveMacroRef();
|
||||
std::vector<MacroRef> _macros;
|
||||
};
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <QDateTime>
|
||||
#include <QThread>
|
||||
#include <curl/curl.h>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "scene-group.hpp"
|
||||
#include "scene-trigger.hpp"
|
||||
@@ -63,6 +64,8 @@ class SwitcherThread;
|
||||
struct SwitcherData {
|
||||
SwitcherThread *th = nullptr;
|
||||
|
||||
bool settingsWindowOpened = false;
|
||||
|
||||
std::condition_variable cv;
|
||||
std::mutex m;
|
||||
bool transitionActive = false;
|
||||
@@ -92,10 +95,14 @@ struct SwitcherData {
|
||||
StartupBehavior startupBehavior = PERSIST;
|
||||
AutoStartEvent autoStartEvent = AutoStartEvent::NEVER;
|
||||
|
||||
std::vector<std::thread> audioHelperThreads;
|
||||
std::atomic_bool masterAudioFadeActive = {false};
|
||||
std::unordered_map<std::string, std::atomic_bool> activeAudioFades;
|
||||
|
||||
Duration cooldown;
|
||||
std::chrono::high_resolution_clock::time_point lastMatchTime;
|
||||
|
||||
std::deque<Macro> macros;
|
||||
std::deque<std::shared_ptr<Macro>> macros;
|
||||
std::condition_variable macroWaitCv;
|
||||
std::atomic_bool abortMacroWait = {false};
|
||||
bool macroSceneSwitched = false;
|
||||
|
||||
@@ -45,6 +45,7 @@ void placeWidgets(std::string text, QBoxLayout *layout,
|
||||
bool addStretch = true);
|
||||
void deleteLayoutItem(QLayoutItem *item);
|
||||
void clearLayout(QLayout *layout, int afterIdx = 0);
|
||||
void setLayoutVisible(QLayout *layout, bool visible);
|
||||
QMetaObject::Connection PulseWidget(QWidget *widget, QColor startColor,
|
||||
QColor = QColor(0, 0, 0, 0),
|
||||
QString specifier = "QLabel ",
|
||||
@@ -78,3 +79,4 @@ void populateFilterSelection(QComboBox *list,
|
||||
void populateSceneItemSelection(QComboBox *list,
|
||||
OBSWeakSource sceneWeakSource = nullptr);
|
||||
void populateSceneItemSelection(QComboBox *list, SceneSelection &s);
|
||||
void populateSourceTypeSelection(QComboBox *list);
|
||||
|
||||
@@ -18,6 +18,94 @@ const static std::map<AudioAction, std::string> actionTypes = {
|
||||
"AdvSceneSwitcher.action.audio.type.masterVolume"},
|
||||
};
|
||||
|
||||
constexpr auto fadeInterval = std::chrono::milliseconds(100);
|
||||
constexpr float minFade = 0.000001f;
|
||||
|
||||
void fadeSourceVolume(Duration duration, float vol, OBSWeakSource audioSource)
|
||||
{
|
||||
auto s = obs_weak_source_get_source(audioSource);
|
||||
if (!s) {
|
||||
return;
|
||||
}
|
||||
float curVol = obs_source_get_volume(s);
|
||||
obs_source_release(s);
|
||||
bool volIncrease = curVol <= vol;
|
||||
int nrSteps = duration.seconds * 1000 / fadeInterval.count();
|
||||
float volDiff = (volIncrease) ? vol - curVol : curVol - vol;
|
||||
float volStep = volDiff / nrSteps;
|
||||
|
||||
if (volStep < minFade) {
|
||||
switcher->activeAudioFades[GetWeakSourceName(audioSource)] =
|
||||
false;
|
||||
return;
|
||||
}
|
||||
|
||||
for (int step = 0; step < nrSteps && !switcher->stop; ++step) {
|
||||
auto s = obs_weak_source_get_source(audioSource);
|
||||
if (!s) {
|
||||
return;
|
||||
}
|
||||
curVol = (volIncrease) ? curVol + volStep : curVol - volStep;
|
||||
obs_source_set_volume(s, curVol);
|
||||
std::this_thread::sleep_for(fadeInterval);
|
||||
obs_source_release(s);
|
||||
}
|
||||
|
||||
switcher->activeAudioFades[GetWeakSourceName(audioSource)] = false;
|
||||
}
|
||||
void fadeMasterVolume(Duration duration, float vol)
|
||||
{
|
||||
float curVol = obs_get_master_volume();
|
||||
bool volIncrease = curVol <= vol;
|
||||
int nrSteps = duration.seconds * 1000 / fadeInterval.count();
|
||||
float volDiff = (volIncrease) ? vol - curVol : curVol - vol;
|
||||
float volStep = volDiff / nrSteps;
|
||||
|
||||
if (volStep < minFade) {
|
||||
switcher->masterAudioFadeActive = false;
|
||||
return;
|
||||
}
|
||||
|
||||
for (int step = 0; step < nrSteps && !switcher->stop; ++step) {
|
||||
curVol = (volIncrease) ? curVol + volStep : curVol - volStep;
|
||||
obs_set_master_volume(curVol);
|
||||
std::this_thread::sleep_for(fadeInterval);
|
||||
}
|
||||
|
||||
switcher->masterAudioFadeActive = false;
|
||||
}
|
||||
|
||||
void startSourceFade(Duration &duration, float vol, OBSWeakSource audioSource)
|
||||
{
|
||||
if (!audioSource) {
|
||||
return;
|
||||
}
|
||||
auto it =
|
||||
switcher->activeAudioFades.find(GetWeakSourceName(audioSource));
|
||||
if (it != switcher->activeAudioFades.end() && it->second == true) {
|
||||
blog(LOG_WARNING,
|
||||
"Audio fade for volume of %s already active! New fade request will be ignored!",
|
||||
GetWeakSourceName(audioSource).c_str());
|
||||
return;
|
||||
}
|
||||
switcher->activeAudioFades[GetWeakSourceName(audioSource)] = true;
|
||||
switcher->audioHelperThreads.emplace_back(fadeSourceVolume, duration,
|
||||
vol, audioSource);
|
||||
}
|
||||
|
||||
void startMasterFade(Duration &duration, float vol)
|
||||
{
|
||||
|
||||
if (switcher->masterAudioFadeActive) {
|
||||
blog(LOG_WARNING,
|
||||
"Audio fade for master volume already active! New fade request will be ignored!");
|
||||
return;
|
||||
}
|
||||
switcher->masterAudioFadeActive = true;
|
||||
switcher->audioHelperThreads.emplace_back(fadeMasterVolume, duration,
|
||||
vol);
|
||||
}
|
||||
|
||||
bool MacroActionAudio::PerformAction()
|
||||
{
|
||||
auto s = obs_weak_source_get_source(_audioSource);
|
||||
@@ -29,10 +117,19 @@ bool MacroActionAudio::PerformAction()
|
||||
obs_source_set_muted(s, false);
|
||||
break;
|
||||
case AudioAction::SOURCE_VOLUME:
|
||||
obs_source_set_volume(s, (float)_volume / 100.0f);
|
||||
if (_fade) {
|
||||
startSourceFade(_duration, (float)_volume / 100.0f,
|
||||
_audioSource);
|
||||
} else {
|
||||
obs_source_set_volume(s, (float)_volume / 100.0f);
|
||||
}
|
||||
break;
|
||||
case AudioAction::MASTER_VOLUME:
|
||||
obs_set_master_volume((float)_volume / 100.0f);
|
||||
if (_fade) {
|
||||
startMasterFade(_duration, (float)_volume / 100.0f);
|
||||
} else {
|
||||
obs_set_master_volume((float)_volume / 100.0f);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@@ -46,9 +143,10 @@ void MacroActionAudio::LogAction()
|
||||
auto it = actionTypes.find(_action);
|
||||
if (it != actionTypes.end()) {
|
||||
vblog(LOG_INFO,
|
||||
"performed action \"%s\" for source \"%s\" with volume %d",
|
||||
"performed action \"%s\" for source \"%s\" with volume %d with fade %d %f",
|
||||
it->second.c_str(),
|
||||
GetWeakSourceName(_audioSource).c_str(), _volume);
|
||||
GetWeakSourceName(_audioSource).c_str(), _volume, _fade,
|
||||
_duration.seconds);
|
||||
} else {
|
||||
blog(LOG_WARNING, "ignored unknown audio action %d",
|
||||
static_cast<int>(_action));
|
||||
@@ -58,20 +156,24 @@ void MacroActionAudio::LogAction()
|
||||
bool MacroActionAudio::Save(obs_data_t *obj)
|
||||
{
|
||||
MacroAction::Save(obj);
|
||||
_duration.Save(obj);
|
||||
obs_data_set_string(obj, "audioSource",
|
||||
GetWeakSourceName(_audioSource).c_str());
|
||||
obs_data_set_int(obj, "action", static_cast<int>(_action));
|
||||
obs_data_set_int(obj, "volume", _volume);
|
||||
obs_data_set_bool(obj, "fade", _fade);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MacroActionAudio::Load(obs_data_t *obj)
|
||||
{
|
||||
MacroAction::Load(obj);
|
||||
_duration.Load(obj);
|
||||
const char *audioSourceName = obs_data_get_string(obj, "audioSource");
|
||||
_audioSource = GetWeakSourceByName(audioSourceName);
|
||||
_action = static_cast<AudioAction>(obs_data_get_int(obj, "action"));
|
||||
_volume = obs_data_get_int(obj, "volume");
|
||||
_fade = obs_data_get_bool(obj, "fade");
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -100,6 +202,8 @@ MacroActionAudioEdit::MacroActionAudioEdit(
|
||||
_volumePercent->setMinimum(0);
|
||||
_volumePercent->setMaximum(2000);
|
||||
_volumePercent->setSuffix("%");
|
||||
_fade = new QCheckBox();
|
||||
_duration = new DurationSelection(parent, false);
|
||||
|
||||
populateActionSelection(_actions);
|
||||
populateAudioSelection(_audioSources);
|
||||
@@ -111,15 +215,25 @@ MacroActionAudioEdit::MacroActionAudioEdit(
|
||||
SLOT(SourceChanged(const QString &)));
|
||||
QWidget::connect(_volumePercent, SIGNAL(valueChanged(int)), this,
|
||||
SLOT(VolumeChanged(int)));
|
||||
QWidget::connect(_fade, SIGNAL(stateChanged(int)), this,
|
||||
SLOT(FadeChanged(int)));
|
||||
QWidget::connect(_duration, SIGNAL(DurationChanged(double)), this,
|
||||
SLOT(DurationChanged(double)));
|
||||
|
||||
QHBoxLayout *mainLayout = new QHBoxLayout;
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{audioSources}}", _audioSources},
|
||||
{"{{actions}}", _actions},
|
||||
{"{{volume}}", _volumePercent},
|
||||
{"{{audioSources}}", _audioSources}, {"{{actions}}", _actions},
|
||||
{"{{volume}}", _volumePercent}, {"{{fade}}", _fade},
|
||||
{"{{duration}}", _duration},
|
||||
};
|
||||
QHBoxLayout *entryLayout = new QHBoxLayout;
|
||||
placeWidgets(obs_module_text("AdvSceneSwitcher.action.audio.entry"),
|
||||
mainLayout, widgetPlaceholders);
|
||||
entryLayout, widgetPlaceholders);
|
||||
_fadeLayout = new QHBoxLayout;
|
||||
placeWidgets(obs_module_text("AdvSceneSwitcher.action.audio.fade"),
|
||||
_fadeLayout, widgetPlaceholders);
|
||||
QVBoxLayout *mainLayout = new QVBoxLayout;
|
||||
mainLayout->addLayout(entryLayout);
|
||||
mainLayout->addLayout(_fadeLayout);
|
||||
setLayout(mainLayout);
|
||||
|
||||
_entryData = entryData;
|
||||
@@ -140,19 +254,11 @@ bool hasSourceControl(AudioAction action)
|
||||
|
||||
void MacroActionAudioEdit::SetWidgetVisibility()
|
||||
{
|
||||
if (hasVolumeControl(_entryData->_action)) {
|
||||
_volumePercent->show();
|
||||
} else {
|
||||
_volumePercent->hide();
|
||||
}
|
||||
|
||||
if (hasSourceControl(_entryData->_action)) {
|
||||
_audioSources->show();
|
||||
} else {
|
||||
_audioSources->hide();
|
||||
}
|
||||
_volumePercent->setVisible(hasVolumeControl(_entryData->_action));
|
||||
_audioSources->setVisible(hasSourceControl(_entryData->_action));
|
||||
setLayoutVisible(_fadeLayout, hasVolumeControl(_entryData->_action));
|
||||
adjustSize();
|
||||
}
|
||||
|
||||
void MacroActionAudioEdit::UpdateEntryData()
|
||||
{
|
||||
if (!_entryData) {
|
||||
@@ -163,7 +269,8 @@ void MacroActionAudioEdit::UpdateEntryData()
|
||||
GetWeakSourceName(_entryData->_audioSource).c_str());
|
||||
_actions->setCurrentIndex(static_cast<int>(_entryData->_action));
|
||||
_volumePercent->setValue(_entryData->_volume);
|
||||
|
||||
_fade->setChecked(_entryData->_fade);
|
||||
_duration->SetDuration(_entryData->_duration);
|
||||
SetWidgetVisibility();
|
||||
}
|
||||
|
||||
@@ -199,3 +306,23 @@ void MacroActionAudioEdit::VolumeChanged(int value)
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_volume = value;
|
||||
}
|
||||
|
||||
void MacroActionAudioEdit::FadeChanged(int value)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_fade = value;
|
||||
}
|
||||
|
||||
void MacroActionAudioEdit::DurationChanged(double seconds)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_duration.seconds = seconds;
|
||||
}
|
||||
|
||||
@@ -92,12 +92,14 @@ void MacroActionEdit::ActionSelectionChanged(const QString &text)
|
||||
return;
|
||||
}
|
||||
|
||||
auto idx = _entryData->get()->GetIndex();
|
||||
std::string id = MacroActionFactory::GetIdByName(text);
|
||||
HeaderInfoChanged("");
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->reset();
|
||||
*_entryData = MacroActionFactory::Create(id);
|
||||
(*_entryData)->SetIndex(idx);
|
||||
auto widget = MacroActionFactory::CreateWidget(id, this, *_entryData);
|
||||
QWidget::connect(widget, SIGNAL(HeaderInfoChanged(const QString &)),
|
||||
this, SLOT(HeaderInfoChanged(const QString &)));
|
||||
|
||||
@@ -94,6 +94,7 @@ MacroActionFilterEdit::MacroActionFilterEdit(
|
||||
{
|
||||
_sources = new QComboBox();
|
||||
_filters = new QComboBox();
|
||||
_filters->setSizeAdjustPolicy(QComboBox::AdjustToContents);
|
||||
_actions = new QComboBox();
|
||||
_getSettings = new QPushButton(
|
||||
obs_module_text("AdvSceneSwitcher.action.filter.getSettings"));
|
||||
@@ -165,6 +166,7 @@ void MacroActionFilterEdit::SourceChanged(const QString &text)
|
||||
}
|
||||
_filters->clear();
|
||||
populateFilterSelection(_filters, _entryData->_source);
|
||||
_filters->adjustSize();
|
||||
}
|
||||
|
||||
void MacroActionFilterEdit::FilterChanged(const QString &text)
|
||||
|
||||
@@ -141,7 +141,7 @@ obs_key_combination keysToOBSKeycombo(const std::vector<HotkeyType> &keys)
|
||||
return combo;
|
||||
}
|
||||
|
||||
for (int i = 0; i < keys.size() - 1; i++) {
|
||||
for (uint32_t i = 0; i < keys.size() - 1; i++) {
|
||||
switch (keys[i]) {
|
||||
case HotkeyType::Key_Shift_L:
|
||||
combo.modifiers |= INTERACT_SHIFT_KEY;
|
||||
|
||||
263
src/macro-action-random.cpp
Normal file
263
src/macro-action-random.cpp
Normal file
@@ -0,0 +1,263 @@
|
||||
#include "headers/macro-action-random.hpp"
|
||||
#include "headers/advanced-scene-switcher.hpp"
|
||||
#include "headers/name-dialog.hpp"
|
||||
#include "headers/utility.hpp"
|
||||
|
||||
#include <cstdlib>
|
||||
|
||||
const std::string MacroActionRandom::id = "random";
|
||||
|
||||
bool MacroActionRandom::_registered = MacroActionFactory::Register(
|
||||
MacroActionRandom::id,
|
||||
{MacroActionRandom::Create, MacroActionRandomEdit::Create,
|
||||
"AdvSceneSwitcher.action.random"});
|
||||
|
||||
std::vector<MacroRef> getPossibleMacros(std::vector<MacroRef> ¯os,
|
||||
MacroRef &lastRandomMacro)
|
||||
{
|
||||
std::vector<MacroRef> res;
|
||||
if (macros.size() == 1) {
|
||||
if (macros[0]->Paused()) {
|
||||
return res;
|
||||
} else {
|
||||
return macros;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto &m : macros) {
|
||||
if (m.get() && !m->Paused() &&
|
||||
!(lastRandomMacro.get() == m.get())) {
|
||||
res.push_back(m);
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
bool MacroActionRandom::PerformAction()
|
||||
{
|
||||
if (_macros.size() == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
auto macros = getPossibleMacros(_macros, lastRandomMacro);
|
||||
if (macros.size() == 0) {
|
||||
return true;
|
||||
}
|
||||
if (macros.size() == 1) {
|
||||
lastRandomMacro = macros[0];
|
||||
return macros[0]->PerformAction();
|
||||
}
|
||||
|
||||
size_t idx = std::rand() % (macros.size());
|
||||
lastRandomMacro = macros[idx];
|
||||
return macros[idx]->PerformAction();
|
||||
}
|
||||
|
||||
void MacroActionRandom::LogAction()
|
||||
{
|
||||
vblog(LOG_INFO, "running random macro");
|
||||
}
|
||||
|
||||
bool MacroActionRandom::Save(obs_data_t *obj)
|
||||
{
|
||||
MacroAction::Save(obj);
|
||||
obs_data_array_t *args = obs_data_array_create();
|
||||
for (auto &m : _macros) {
|
||||
obs_data_t *array_obj = obs_data_create();
|
||||
m.Save(array_obj);
|
||||
obs_data_array_push_back(args, array_obj);
|
||||
obs_data_release(array_obj);
|
||||
}
|
||||
obs_data_set_array(obj, "macros", args);
|
||||
obs_data_array_release(args);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MacroActionRandom::Load(obs_data_t *obj)
|
||||
{
|
||||
MacroAction::Load(obj);
|
||||
obs_data_array_t *args = obs_data_get_array(obj, "macros");
|
||||
size_t count = obs_data_array_count(args);
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
obs_data_t *array_obj = obs_data_array_item(args, i);
|
||||
MacroRef ref;
|
||||
ref.Load(array_obj);
|
||||
_macros.push_back(ref);
|
||||
obs_data_release(array_obj);
|
||||
}
|
||||
obs_data_array_release(args);
|
||||
return true;
|
||||
}
|
||||
|
||||
MacroActionRandomEdit::MacroActionRandomEdit(
|
||||
QWidget *parent, std::shared_ptr<MacroActionRandom> entryData)
|
||||
: QWidget(parent)
|
||||
{
|
||||
_macroSelection = new MacroSelection(window());
|
||||
_macroList = new QListWidget();
|
||||
_macroList->setSortingEnabled(true);
|
||||
_add = new QPushButton();
|
||||
_add->setMaximumSize(QSize(22, 22));
|
||||
_add->setProperty("themeID",
|
||||
QVariant(QString::fromUtf8("addIconSmall")));
|
||||
_add->setFlat(true);
|
||||
_remove = new QPushButton();
|
||||
_remove->setMaximumSize(QSize(22, 22));
|
||||
_remove->setProperty("themeID",
|
||||
QVariant(QString::fromUtf8("removeIconSmall")));
|
||||
_remove->setFlat(true);
|
||||
|
||||
QWidget::connect(_add, SIGNAL(clicked()), this, SLOT(AddMacro()));
|
||||
QWidget::connect(_remove, SIGNAL(clicked()), this, SLOT(RemoveMacro()));
|
||||
QWidget::connect(_macroList, SIGNAL(currentRowChanged(int)), this,
|
||||
SLOT(MacroSelectionChanged(int)));
|
||||
QWidget::connect(window(),
|
||||
SIGNAL(MacroRenamed(const QString &, const QString &)),
|
||||
this,
|
||||
SLOT(MacroRename(const QString &, const QString &)));
|
||||
|
||||
auto *entryLayout = new QHBoxLayout;
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{macroSelection}}", _macroSelection},
|
||||
};
|
||||
placeWidgets(obs_module_text("AdvSceneSwitcher.action.random.entry"),
|
||||
entryLayout, widgetPlaceholders);
|
||||
|
||||
auto *argButtonLayout = new QHBoxLayout;
|
||||
argButtonLayout->addWidget(_add);
|
||||
argButtonLayout->addWidget(_remove);
|
||||
argButtonLayout->addStretch();
|
||||
|
||||
auto *mainLayout = new QVBoxLayout;
|
||||
mainLayout->addLayout(entryLayout);
|
||||
mainLayout->addWidget(new QLabel(
|
||||
obs_module_text("AdvSceneSwitcher.action.random.arguments")));
|
||||
mainLayout->addWidget(_macroList);
|
||||
mainLayout->addLayout(argButtonLayout);
|
||||
setLayout(mainLayout);
|
||||
|
||||
_entryData = entryData;
|
||||
UpdateEntryData();
|
||||
_loading = false;
|
||||
}
|
||||
|
||||
void MacroActionRandomEdit::UpdateEntryData()
|
||||
{
|
||||
if (!_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto &m : _entryData->_macros) {
|
||||
if (!m.get()) {
|
||||
continue;
|
||||
}
|
||||
auto name = QString::fromStdString(m->Name());
|
||||
QListWidgetItem *item = new QListWidgetItem(name, _macroList);
|
||||
item->setData(Qt::UserRole, name);
|
||||
}
|
||||
}
|
||||
|
||||
void MacroActionRandomEdit::MacroRemove(const QString &name)
|
||||
{
|
||||
if (_entryData) {
|
||||
auto it = _entryData->_macros.begin();
|
||||
while (it != _entryData->_macros.end()) {
|
||||
if (it->get()->Name() == name.toStdString()) {
|
||||
it = _entryData->_macros.erase(it);
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MacroActionRandomEdit::MacroRename(const QString &oldName,
|
||||
const QString &newName)
|
||||
{
|
||||
auto count = _macroList->count();
|
||||
for (int idx = 0; idx < count; ++idx) {
|
||||
QListWidgetItem *item = _macroList->item(idx);
|
||||
QString itemString = item->data(Qt::UserRole).toString();
|
||||
if (oldName == itemString) {
|
||||
item->setData(Qt::UserRole, newName);
|
||||
item->setText(newName);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MacroActionRandomEdit::AddMacro()
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto macroName = _macroSelection->currentText();
|
||||
MacroRef macro(macroName.toStdString());
|
||||
|
||||
if (!macro.get()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (FindEntry(macro->Name()) != -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
QVariant v = QVariant::fromValue(macroName);
|
||||
QListWidgetItem *item = new QListWidgetItem(macroName, _macroList);
|
||||
item->setData(Qt::UserRole, macroName);
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_macros.push_back(macro);
|
||||
}
|
||||
|
||||
void MacroActionRandomEdit::RemoveMacro()
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
auto item = _macroList->currentItem();
|
||||
if (!item) {
|
||||
return;
|
||||
}
|
||||
std::string name = item->data(Qt::UserRole).toString().toStdString();
|
||||
for (auto it = _entryData->_macros.begin();
|
||||
it != _entryData->_macros.end(); ++it) {
|
||||
auto m = *it;
|
||||
if (m.get() && m->Name() == name) {
|
||||
_entryData->_macros.erase(it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
delete item;
|
||||
}
|
||||
|
||||
int MacroActionRandomEdit::FindEntry(const std::string ¯o)
|
||||
{
|
||||
int count = _macroList->count();
|
||||
int idx = -1;
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
QListWidgetItem *item = _macroList->item(i);
|
||||
QString itemString = item->data(Qt::UserRole).toString();
|
||||
if (QString::fromStdString(macro) == itemString) {
|
||||
idx = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return idx;
|
||||
}
|
||||
|
||||
void MacroActionRandomEdit::MacroSelectionChanged(int idx)
|
||||
{
|
||||
if (_loading || !_entryData || idx == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
QListWidgetItem *item = _macroList->item(idx);
|
||||
QString name = item->text();
|
||||
_macroSelection->SetCurrentMacro(GetMacroByName(name.toUtf8().data()));
|
||||
}
|
||||
@@ -17,14 +17,22 @@ const static std::map<SceneVisibilityAction, std::string> actionTypes = {
|
||||
"AdvSceneSwitcher.action.sceneVisibility.type.hide"},
|
||||
};
|
||||
|
||||
struct VisInfo {
|
||||
const static std::map<SceneItemSourceType, std::string> sourceItemSourceTypes = {
|
||||
{SceneItemSourceType::SOURCE,
|
||||
"AdvSceneSwitcher.action.sceneVisibility.type.source"},
|
||||
{SceneItemSourceType::SOURCE_GROUP,
|
||||
"AdvSceneSwitcher.action.sceneVisibility.type.sourceGroup"},
|
||||
};
|
||||
|
||||
struct VisibilityData {
|
||||
std::string name;
|
||||
bool visible;
|
||||
};
|
||||
|
||||
static bool visibilityEnum(obs_scene_t *, obs_sceneitem_t *item, void *ptr)
|
||||
static bool visibilitySourceEnum(obs_scene_t *, obs_sceneitem_t *item,
|
||||
void *ptr)
|
||||
{
|
||||
VisInfo *vInfo = reinterpret_cast<VisInfo *>(ptr);
|
||||
VisibilityData *vInfo = reinterpret_cast<VisibilityData *>(ptr);
|
||||
auto sourceName = obs_source_get_name(obs_sceneitem_get_source(item));
|
||||
if (vInfo->name == sourceName) {
|
||||
obs_sceneitem_set_visible(item, vInfo->visible);
|
||||
@@ -32,7 +40,26 @@ static bool visibilityEnum(obs_scene_t *, obs_sceneitem_t *item, void *ptr)
|
||||
|
||||
if (obs_sceneitem_is_group(item)) {
|
||||
obs_scene_t *scene = obs_sceneitem_group_get_scene(item);
|
||||
obs_scene_enum_items(scene, visibilityEnum, ptr);
|
||||
obs_scene_enum_items(scene, visibilitySourceEnum, ptr);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool visibilitySourceTypeEnum(obs_scene_t *, obs_sceneitem_t *item,
|
||||
void *ptr)
|
||||
{
|
||||
VisibilityData *vInfo = reinterpret_cast<VisibilityData *>(ptr);
|
||||
|
||||
auto sourceTypeName = obs_source_get_display_name(
|
||||
obs_source_get_id(obs_sceneitem_get_source(item)));
|
||||
if (vInfo->name == sourceTypeName) {
|
||||
obs_sceneitem_set_visible(item, vInfo->visible);
|
||||
}
|
||||
|
||||
if (obs_sceneitem_is_group(item)) {
|
||||
obs_scene_t *scene = obs_sceneitem_group_get_scene(item);
|
||||
obs_scene_enum_items(scene, visibilitySourceTypeEnum, ptr);
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -43,18 +70,21 @@ bool MacroActionSceneVisibility::PerformAction()
|
||||
auto s = obs_weak_source_get_source(_scene.GetScene());
|
||||
auto scene = obs_scene_from_source(s);
|
||||
auto sourceName = GetWeakSourceName(_source);
|
||||
VisInfo vInfo = {sourceName, _action == SceneVisibilityAction::SHOW};
|
||||
VisibilityData vInfo = {"", _action == SceneVisibilityAction::SHOW};
|
||||
|
||||
switch (_action) {
|
||||
case SceneVisibilityAction::SHOW:
|
||||
obs_scene_enum_items(scene, visibilityEnum, &vInfo);
|
||||
switch (_sourceType) {
|
||||
case SceneItemSourceType::SOURCE:
|
||||
vInfo.name = sourceName;
|
||||
obs_scene_enum_items(scene, visibilitySourceEnum, &vInfo);
|
||||
break;
|
||||
case SceneVisibilityAction::HIDE:
|
||||
obs_scene_enum_items(scene, visibilityEnum, &vInfo);
|
||||
case SceneItemSourceType::SOURCE_GROUP:
|
||||
vInfo.name = _sourceGroup;
|
||||
obs_scene_enum_items(scene, visibilitySourceTypeEnum, &vInfo);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
obs_source_release(s);
|
||||
return true;
|
||||
}
|
||||
@@ -63,10 +93,18 @@ void MacroActionSceneVisibility::LogAction()
|
||||
{
|
||||
auto it = actionTypes.find(_action);
|
||||
if (it != actionTypes.end()) {
|
||||
vblog(LOG_INFO,
|
||||
"performed visibility action \"%s\" for source \"%s\" on scene \"%s\"",
|
||||
it->second.c_str(), GetWeakSourceName(_source).c_str(),
|
||||
_scene.ToString().c_str());
|
||||
if (_sourceType == SceneItemSourceType::SOURCE) {
|
||||
vblog(LOG_INFO,
|
||||
"performed visibility action \"%s\" for source \"%s\" on scene \"%s\"",
|
||||
it->second.c_str(),
|
||||
GetWeakSourceName(_source).c_str(),
|
||||
_scene.ToString().c_str());
|
||||
} else {
|
||||
vblog(LOG_INFO,
|
||||
"performed visibility action \"%s\" for any source type \"%s\" on scene \"%s\"",
|
||||
it->second.c_str(), _sourceGroup.c_str(),
|
||||
_scene.ToString().c_str());
|
||||
}
|
||||
} else {
|
||||
blog(LOG_WARNING, "ignored unknown SceneVisibility action %d",
|
||||
static_cast<int>(_action));
|
||||
@@ -77,8 +115,14 @@ bool MacroActionSceneVisibility::Save(obs_data_t *obj)
|
||||
{
|
||||
MacroAction::Save(obj);
|
||||
_scene.Save(obj);
|
||||
obs_data_set_string(obj, "source", GetWeakSourceName(_source).c_str());
|
||||
if (_sourceType == SceneItemSourceType::SOURCE) {
|
||||
obs_data_set_string(obj, "source",
|
||||
GetWeakSourceName(_source).c_str());
|
||||
} else {
|
||||
obs_data_set_string(obj, "source", _sourceGroup.c_str());
|
||||
}
|
||||
obs_data_set_int(obj, "action", static_cast<int>(_action));
|
||||
obs_data_set_int(obj, "sourceType", static_cast<int>(_sourceType));
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -86,8 +130,11 @@ bool MacroActionSceneVisibility::Load(obs_data_t *obj)
|
||||
{
|
||||
MacroAction::Load(obj);
|
||||
_scene.Load(obj);
|
||||
_sourceType = static_cast<SceneItemSourceType>(
|
||||
obs_data_get_int(obj, "sourceType"));
|
||||
const char *sourceName = obs_data_get_string(obj, "source");
|
||||
_source = GetWeakSourceByName(sourceName);
|
||||
_sourceGroup = sourceName;
|
||||
_action = static_cast<SceneVisibilityAction>(
|
||||
obs_data_get_int(obj, "action"));
|
||||
return true;
|
||||
@@ -95,9 +142,16 @@ bool MacroActionSceneVisibility::Load(obs_data_t *obj)
|
||||
|
||||
std::string MacroActionSceneVisibility::GetShortDesc()
|
||||
{
|
||||
if (_source) {
|
||||
if (_sourceType == SceneItemSourceType::SOURCE && _source) {
|
||||
return _scene.ToString() + " - " + GetWeakSourceName(_source);
|
||||
}
|
||||
if (_sourceType == SceneItemSourceType::SOURCE_GROUP &&
|
||||
!_sourceGroup.empty()) {
|
||||
return _scene.ToString() + " - " +
|
||||
obs_module_text(
|
||||
"AdvSceneSwitcher.action.sceneVisibility.type.sourceGroup") +
|
||||
" " + _sourceGroup;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
@@ -108,31 +162,44 @@ static inline void populateActionSelection(QComboBox *list)
|
||||
}
|
||||
}
|
||||
|
||||
static inline void populateSourceItemTypeSelection(QComboBox *list)
|
||||
{
|
||||
for (auto entry : sourceItemSourceTypes) {
|
||||
list->addItem(obs_module_text(entry.second.c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
MacroActionSceneVisibilityEdit::MacroActionSceneVisibilityEdit(
|
||||
QWidget *parent, std::shared_ptr<MacroActionSceneVisibility> entryData)
|
||||
: QWidget(parent)
|
||||
{
|
||||
_scenes = new SceneSelectionWidget(window(), false, false, true);
|
||||
_scenes = new SceneSelectionWidget(window(), false, true, true);
|
||||
_sourceTypes = new QComboBox();
|
||||
_sources = new QComboBox();
|
||||
_sources->setSizeAdjustPolicy(QComboBox::AdjustToContents);
|
||||
_actions = new QComboBox();
|
||||
|
||||
populateSourceItemTypeSelection(_sourceTypes);
|
||||
populateActionSelection(_actions);
|
||||
|
||||
QWidget::connect(_actions, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(ActionChanged(int)));
|
||||
QWidget::connect(_scenes, SIGNAL(SceneChanged(const SceneSelection &)),
|
||||
this, SLOT(SceneChanged(const SceneSelection &)));
|
||||
QWidget::connect(_sourceTypes, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(SourceTypeChanged(int)));
|
||||
QWidget::connect(_sources, SIGNAL(currentTextChanged(const QString &)),
|
||||
this, SLOT(SourceChanged(const QString &)));
|
||||
|
||||
QHBoxLayout *mainLayout = new QHBoxLayout;
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{scenes}}", _scenes},
|
||||
{"{{sourceTypes}}", _sourceTypes},
|
||||
{"{{sources}}", _sources},
|
||||
{"{{actions}}", _actions},
|
||||
};
|
||||
placeWidgets(obs_module_text(
|
||||
"AdvSceneSwitcher.action.SceneVisibility.entry"),
|
||||
"AdvSceneSwitcher.action.sceneVisibility.entry"),
|
||||
mainLayout, widgetPlaceholders);
|
||||
setLayout(mainLayout);
|
||||
|
||||
@@ -148,10 +215,17 @@ void MacroActionSceneVisibilityEdit::UpdateEntryData()
|
||||
}
|
||||
|
||||
_actions->setCurrentIndex(static_cast<int>(_entryData->_action));
|
||||
_sourceTypes->setCurrentIndex(
|
||||
static_cast<int>(_entryData->_sourceType));
|
||||
_scenes->SetScene(_entryData->_scene);
|
||||
populateSceneItemSelection(_sources, _entryData->_scene);
|
||||
_sources->setCurrentText(
|
||||
GetWeakSourceName(_entryData->_source).c_str());
|
||||
if (_entryData->_sourceType == SceneItemSourceType::SOURCE) {
|
||||
populateSceneItemSelection(_sources, _entryData->_scene);
|
||||
_sources->setCurrentText(
|
||||
GetWeakSourceName(_entryData->_source).c_str());
|
||||
} else {
|
||||
populateSourceTypeSelection(_sources);
|
||||
_sources->setCurrentText(_entryData->_sourceGroup.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void MacroActionSceneVisibilityEdit::SceneChanged(const SceneSelection &s)
|
||||
@@ -165,6 +239,26 @@ void MacroActionSceneVisibilityEdit::SceneChanged(const SceneSelection &s)
|
||||
}
|
||||
_sources->clear();
|
||||
populateSceneItemSelection(_sources, _entryData->_scene);
|
||||
_sources->adjustSize();
|
||||
}
|
||||
|
||||
void MacroActionSceneVisibilityEdit::SourceTypeChanged(int value)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_sourceType =
|
||||
static_cast<SceneItemSourceType>(value);
|
||||
}
|
||||
|
||||
_sources->clear();
|
||||
if (_entryData->_sourceType == SceneItemSourceType::SOURCE) {
|
||||
populateSceneItemSelection(_sources, _entryData->_scene);
|
||||
} else {
|
||||
populateSourceTypeSelection(_sources);
|
||||
}
|
||||
}
|
||||
|
||||
void MacroActionSceneVisibilityEdit::SourceChanged(const QString &text)
|
||||
@@ -174,7 +268,15 @@ void MacroActionSceneVisibilityEdit::SourceChanged(const QString &text)
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_source = GetWeakSourceByQString(text);
|
||||
if (_entryData->_sourceType == SceneItemSourceType::SOURCE) {
|
||||
_entryData->_source = GetWeakSourceByQString(text);
|
||||
} else {
|
||||
if (text == obs_module_text("AdvSceneSwitcher.selectItem")) {
|
||||
_entryData->_sourceGroup = "";
|
||||
} else {
|
||||
_entryData->_sourceGroup = text.toStdString();
|
||||
}
|
||||
}
|
||||
emit HeaderInfoChanged(
|
||||
QString::fromStdString(_entryData->GetShortDesc()));
|
||||
}
|
||||
|
||||
@@ -188,6 +188,7 @@ void MacroConditionEdit::ConditionSelectionChanged(const QString &text)
|
||||
return;
|
||||
}
|
||||
|
||||
auto idx = _entryData->get()->GetIndex();
|
||||
std::string id = MacroConditionFactory::GetIdByName(text);
|
||||
|
||||
auto temp = DurationConstraint();
|
||||
@@ -198,6 +199,7 @@ void MacroConditionEdit::ConditionSelectionChanged(const QString &text)
|
||||
auto logic = (*_entryData)->GetLogicType();
|
||||
_entryData->reset();
|
||||
*_entryData = MacroConditionFactory::Create(id);
|
||||
(*_entryData)->SetIndex(idx);
|
||||
(*_entryData)->SetLogicType(logic);
|
||||
auto widget =
|
||||
MacroConditionFactory::CreateWidget(id, this, *_entryData);
|
||||
|
||||
@@ -96,6 +96,7 @@ MacroConditionFilterEdit::MacroConditionFilterEdit(
|
||||
{
|
||||
_sources = new QComboBox();
|
||||
_filters = new QComboBox();
|
||||
_filters->setSizeAdjustPolicy(QComboBox::AdjustToContents);
|
||||
_conditions = new QComboBox();
|
||||
_getSettings = new QPushButton(obs_module_text(
|
||||
"AdvSceneSwitcher.condition.filter.getSettings"));
|
||||
@@ -159,6 +160,7 @@ void MacroConditionFilterEdit::SourceChanged(const QString &text)
|
||||
}
|
||||
_filters->clear();
|
||||
populateFilterSelection(_filters, _entryData->_source);
|
||||
_filters->adjustSize();
|
||||
}
|
||||
|
||||
void MacroConditionFilterEdit::FilterChanged(const QString &text)
|
||||
|
||||
@@ -125,6 +125,8 @@ MacroConditionMacroEdit::MacroConditionMacroEdit(
|
||||
_currentCount = new QLabel(parent);
|
||||
_resetCount = new QPushButton(obs_module_text(
|
||||
"AdvSceneSwitcher.condition.macro.count.reset"));
|
||||
_pausedWarning = new QLabel(obs_module_text(
|
||||
"AdvSceneSwitcher.condition.macro.pausedWarning"));
|
||||
|
||||
_count->setMaximum(10000000);
|
||||
populateTypeSelection(_types);
|
||||
@@ -156,9 +158,18 @@ MacroConditionMacroEdit::MacroConditionMacroEdit(
|
||||
mainLayout->addLayout(typesLayout);
|
||||
mainLayout->addLayout(_settingsLine1);
|
||||
mainLayout->addLayout(_settingsLine2);
|
||||
mainLayout->addWidget(_pausedWarning);
|
||||
setLayout(mainLayout);
|
||||
|
||||
_entryData = entryData;
|
||||
|
||||
connect(&_countTimer, SIGNAL(timeout()), this, SLOT(UpdateCount()));
|
||||
_countTimer.start(1000);
|
||||
|
||||
_pausedWarning->setVisible(false);
|
||||
connect(&_pausedTimer, SIGNAL(timeout()), this, SLOT(UpdatePaused()));
|
||||
_pausedTimer.start(1000);
|
||||
|
||||
UpdateEntryData();
|
||||
_loading = false;
|
||||
}
|
||||
@@ -237,7 +248,6 @@ void MacroConditionMacroEdit::UpdateEntryData()
|
||||
_counterConditions->setCurrentIndex(
|
||||
static_cast<int>(_entryData->_counterCondition));
|
||||
_count->setValue(_entryData->_count);
|
||||
ResetTimer();
|
||||
}
|
||||
|
||||
void MacroConditionMacroEdit::MacroChanged(const QString &text)
|
||||
@@ -248,7 +258,6 @@ void MacroConditionMacroEdit::MacroChanged(const QString &text)
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_macro.UpdateRef(text);
|
||||
ResetTimer();
|
||||
emit HeaderInfoChanged(
|
||||
QString::fromStdString(_entryData->GetShortDesc()));
|
||||
}
|
||||
@@ -303,7 +312,6 @@ void MacroConditionMacroEdit::ResetClicked()
|
||||
}
|
||||
|
||||
_entryData->_macro->ResetCount();
|
||||
ResetTimer();
|
||||
}
|
||||
|
||||
void MacroConditionMacroEdit::UpdateCount()
|
||||
@@ -316,9 +324,9 @@ void MacroConditionMacroEdit::UpdateCount()
|
||||
}
|
||||
}
|
||||
|
||||
void MacroConditionMacroEdit::ResetTimer()
|
||||
void MacroConditionMacroEdit::UpdatePaused()
|
||||
{
|
||||
_timer.reset(new QTimer(this));
|
||||
connect(_timer.get(), SIGNAL(timeout()), this, SLOT(UpdateCount()));
|
||||
_timer->start(1000);
|
||||
_pausedWarning->setVisible(_entryData && _entryData->_macro.get() &&
|
||||
_entryData->_macro->Paused());
|
||||
adjustSize();
|
||||
}
|
||||
|
||||
184
src/macro-condition-scene-visibility.cpp
Normal file
184
src/macro-condition-scene-visibility.cpp
Normal file
@@ -0,0 +1,184 @@
|
||||
#include "headers/macro-condition-edit.hpp"
|
||||
#include "headers/macro-condition-scene-visibility.hpp"
|
||||
#include "headers/utility.hpp"
|
||||
#include "headers/advanced-scene-switcher.hpp"
|
||||
|
||||
#include <regex>
|
||||
|
||||
const std::string MacroConditionSceneVisibility::id = "scene_visibility";
|
||||
|
||||
bool MacroConditionSceneVisibility::_registered =
|
||||
MacroConditionFactory::Register(
|
||||
MacroConditionSceneVisibility::id,
|
||||
{MacroConditionSceneVisibility::Create,
|
||||
MacroConditionSceneVisibilityEdit::Create,
|
||||
"AdvSceneSwitcher.condition.sceneVisibility"});
|
||||
|
||||
static std::map<SceneVisibilityCondition, std::string>
|
||||
SceneVisibilityConditionTypes = {
|
||||
{SceneVisibilityCondition::SHOWN,
|
||||
"AdvSceneSwitcher.condition.sceneVisibility.type.shown"},
|
||||
{SceneVisibilityCondition::HIDDEN,
|
||||
"AdvSceneSwitcher.condition.sceneVisibility.type.hidden"},
|
||||
};
|
||||
|
||||
struct VisibilityData {
|
||||
std::string name;
|
||||
bool visible;
|
||||
bool result;
|
||||
};
|
||||
|
||||
static bool visibilityEnum(obs_scene_t *, obs_sceneitem_t *item, void *ptr)
|
||||
{
|
||||
VisibilityData *vInfo = reinterpret_cast<VisibilityData *>(ptr);
|
||||
auto sourceName = obs_source_get_name(obs_sceneitem_get_source(item));
|
||||
if (vInfo->name == sourceName &&
|
||||
obs_sceneitem_visible(item) == vInfo->visible) {
|
||||
vInfo->result = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (obs_sceneitem_is_group(item)) {
|
||||
obs_scene_t *scene = obs_sceneitem_group_get_scene(item);
|
||||
obs_scene_enum_items(scene, visibilityEnum, ptr);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MacroConditionSceneVisibility::CheckCondition()
|
||||
{
|
||||
if (!_source) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto s = obs_weak_source_get_source(_scene.GetScene());
|
||||
auto scene = obs_scene_from_source(s);
|
||||
auto sourceName = GetWeakSourceName(_source);
|
||||
VisibilityData data = {sourceName,
|
||||
_condition == SceneVisibilityCondition::SHOWN,
|
||||
false};
|
||||
obs_scene_enum_items(scene, visibilityEnum, &data);
|
||||
obs_source_release(s);
|
||||
return data.result;
|
||||
}
|
||||
|
||||
bool MacroConditionSceneVisibility::Save(obs_data_t *obj)
|
||||
{
|
||||
MacroCondition::Save(obj);
|
||||
_scene.Save(obj);
|
||||
obs_data_set_string(obj, "source", GetWeakSourceName(_source).c_str());
|
||||
|
||||
obs_data_set_int(obj, "condition", static_cast<int>(_condition));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MacroConditionSceneVisibility::Load(obs_data_t *obj)
|
||||
{
|
||||
MacroCondition::Load(obj);
|
||||
_scene.Load(obj);
|
||||
const char *sourceName = obs_data_get_string(obj, "source");
|
||||
_source = GetWeakSourceByName(sourceName);
|
||||
_condition = static_cast<SceneVisibilityCondition>(
|
||||
obs_data_get_int(obj, "condition"));
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string MacroConditionSceneVisibility::GetShortDesc()
|
||||
{
|
||||
if (_source) {
|
||||
return _scene.ToString() + " - " + GetWeakSourceName(_source);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
static inline void populateConditionSelection(QComboBox *list)
|
||||
{
|
||||
for (auto entry : SceneVisibilityConditionTypes) {
|
||||
list->addItem(obs_module_text(entry.second.c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
MacroConditionSceneVisibilityEdit::MacroConditionSceneVisibilityEdit(
|
||||
QWidget *parent,
|
||||
std::shared_ptr<MacroConditionSceneVisibility> entryData)
|
||||
: QWidget(parent)
|
||||
{
|
||||
_scenes = new SceneSelectionWidget(window(), false, true, true);
|
||||
_sources = new QComboBox();
|
||||
_sources->setSizeAdjustPolicy(QComboBox::AdjustToContents);
|
||||
_conditions = new QComboBox();
|
||||
|
||||
populateConditionSelection(_conditions);
|
||||
|
||||
QWidget::connect(_scenes, SIGNAL(SceneChanged(const SceneSelection &)),
|
||||
this, SLOT(SceneChanged(const SceneSelection &)));
|
||||
QWidget::connect(_sources, SIGNAL(currentTextChanged(const QString &)),
|
||||
this, SLOT(SourceChanged(const QString &)));
|
||||
QWidget::connect(_conditions, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(ConditionChanged(int)));
|
||||
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{sources}}", _sources},
|
||||
{"{{scenes}}", _scenes},
|
||||
{"{{conditions}}", _conditions},
|
||||
};
|
||||
QHBoxLayout *mainLayout = new QHBoxLayout;
|
||||
placeWidgets(
|
||||
obs_module_text(
|
||||
"AdvSceneSwitcher.condition.sceneVisibility.entry"),
|
||||
mainLayout, widgetPlaceholders);
|
||||
setLayout(mainLayout);
|
||||
|
||||
_entryData = entryData;
|
||||
UpdateEntryData();
|
||||
_loading = false;
|
||||
}
|
||||
|
||||
void MacroConditionSceneVisibilityEdit::SourceChanged(const QString &text)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_source = GetWeakSourceByQString(text);
|
||||
}
|
||||
|
||||
void MacroConditionSceneVisibilityEdit::SceneChanged(const SceneSelection &s)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_scene = s;
|
||||
}
|
||||
_sources->clear();
|
||||
populateSceneItemSelection(_sources, _entryData->_scene);
|
||||
_sources->adjustSize();
|
||||
}
|
||||
|
||||
void MacroConditionSceneVisibilityEdit::ConditionChanged(int index)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
_entryData->_condition = static_cast<SceneVisibilityCondition>(index);
|
||||
}
|
||||
|
||||
void MacroConditionSceneVisibilityEdit::UpdateEntryData()
|
||||
{
|
||||
if (!_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
_conditions->setCurrentIndex(static_cast<int>(_entryData->_condition));
|
||||
_scenes->SetScene(_entryData->_scene);
|
||||
populateSceneItemSelection(_sources, _entryData->_scene);
|
||||
_sources->setCurrentText(
|
||||
GetWeakSourceName(_entryData->_source).c_str());
|
||||
}
|
||||
@@ -947,16 +947,6 @@ bool patternControlIsOptional(VideoCondition cond)
|
||||
cond == VideoCondition::HAS_NOT_CHANGED;
|
||||
}
|
||||
|
||||
void setLayoutVisible(QLayout *layout, bool visible)
|
||||
{
|
||||
for (int i = 0; i < layout->count(); ++i) {
|
||||
QWidget *widget = layout->itemAt(i)->widget();
|
||||
if (widget != NULL) {
|
||||
widget->setVisible(visible);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MacroConditionVideoEdit::SetWidgetVisibility()
|
||||
{
|
||||
_imagePath->setVisible(requiresFileInput(_entryData->_condition));
|
||||
|
||||
@@ -16,7 +16,7 @@ MacroSelection::MacroSelection(QWidget *parent) : QComboBox(parent)
|
||||
firstItem->setEnabled(false);
|
||||
|
||||
for (auto &m : switcher->macros) {
|
||||
addItem(QString::fromStdString(m.Name()));
|
||||
addItem(QString::fromStdString(m->Name()));
|
||||
}
|
||||
|
||||
QWidget::connect(parent, SIGNAL(MacroAdded(const QString &)), this,
|
||||
|
||||
@@ -47,7 +47,7 @@ bool AdvSceneSwitcher::addNewMacro(std::string &name)
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
switcher->macros.emplace_back(name);
|
||||
switcher->macros.emplace_back(std::make_shared<Macro>(name));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -84,8 +84,11 @@ void AdvSceneSwitcher::on_macroRemove_clicked()
|
||||
switcher->abortMacroWait = true;
|
||||
switcher->macroWaitCv.notify_one();
|
||||
int idx = ui->macros->currentRow();
|
||||
QString::fromStdString(switcher->macros[idx].Name());
|
||||
QString::fromStdString(switcher->macros[idx]->Name());
|
||||
switcher->macros.erase(switcher->macros.begin() + idx);
|
||||
for (auto &m : switcher->macros) {
|
||||
m->ResolveMacroRef();
|
||||
}
|
||||
}
|
||||
|
||||
delete item;
|
||||
@@ -108,7 +111,7 @@ void AdvSceneSwitcher::on_macroUp_clicked()
|
||||
switcher->macros.begin() + index - 1);
|
||||
|
||||
for (auto &m : switcher->macros) {
|
||||
m.ResolveMacroRef();
|
||||
m->ResolveMacroRef();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -125,7 +128,7 @@ void AdvSceneSwitcher::on_macroDown_clicked()
|
||||
switcher->macros.begin() + index + 1);
|
||||
|
||||
for (auto &m : switcher->macros) {
|
||||
m.ResolveMacroRef();
|
||||
m->ResolveMacroRef();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -170,9 +173,8 @@ void AdvSceneSwitcher::on_macroName_editingFinished()
|
||||
emit MacroRenamed(oldName, newName);
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::PopulateMacroActions(Macro &m, int afterIdx)
|
||||
void AdvSceneSwitcher::PopulateMacroActions(Macro &m, uint32_t afterIdx)
|
||||
{
|
||||
bool root = afterIdx == 0;
|
||||
auto &actions = m.Actions();
|
||||
for (; afterIdx < actions.size(); afterIdx++) {
|
||||
auto newEntry = new MacroActionEdit(this, &actions[afterIdx],
|
||||
@@ -180,12 +182,11 @@ void AdvSceneSwitcher::PopulateMacroActions(Macro &m, int afterIdx)
|
||||
ConnectControlSignals(newEntry);
|
||||
ui->macroEditActionLayout->addWidget(newEntry);
|
||||
ui->macroEditActionHelp->setVisible(false);
|
||||
root = false;
|
||||
}
|
||||
ui->macroEditActionHelp->setVisible(actions.size() == 0);
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::PopulateMacroConditions(Macro &m, int afterIdx)
|
||||
void AdvSceneSwitcher::PopulateMacroConditions(Macro &m, uint32_t afterIdx)
|
||||
{
|
||||
bool root = afterIdx == 0;
|
||||
auto &conditions = m.Conditions();
|
||||
@@ -314,12 +315,12 @@ void AdvSceneSwitcher::on_macros_itemChanged(QListWidgetItem *item)
|
||||
void AdvSceneSwitcher::setupMacroTab()
|
||||
{
|
||||
for (auto &m : switcher->macros) {
|
||||
QString text = QString::fromStdString(m.Name());
|
||||
QString text = QString::fromStdString(m->Name());
|
||||
|
||||
QListWidgetItem *item = new QListWidgetItem(text, ui->macros);
|
||||
item->setData(Qt::UserRole, text);
|
||||
item->setFlags(item->flags() | Qt::ItemIsUserCheckable);
|
||||
if (m.Paused()) {
|
||||
if (m->Paused()) {
|
||||
item->setCheckState(Qt::Unchecked);
|
||||
} else {
|
||||
item->setCheckState(Qt::Checked);
|
||||
@@ -392,8 +393,8 @@ void AdvSceneSwitcher::CopyMacro()
|
||||
return;
|
||||
}
|
||||
|
||||
switcher->macros.back().Load(data);
|
||||
switcher->macros.back().SetName(name);
|
||||
switcher->macros.back()->Load(data);
|
||||
switcher->macros.back()->SetName(name);
|
||||
obs_data_release(data);
|
||||
|
||||
QString text = QString::fromStdString(name);
|
||||
|
||||
@@ -36,6 +36,11 @@ bool Macro::CeckMatch()
|
||||
{
|
||||
_matched = false;
|
||||
for (auto &c : _conditions) {
|
||||
if (_paused) {
|
||||
vblog(LOG_INFO, "Macro %s is paused", _name.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
auto startTime = std::chrono::high_resolution_clock::now();
|
||||
bool cond = c->CheckCondition();
|
||||
auto endTime = std::chrono::high_resolution_clock::now();
|
||||
@@ -89,15 +94,11 @@ bool Macro::CeckMatch()
|
||||
|
||||
vblog(LOG_INFO, "Macro %s returned %d", _name.c_str(), _matched);
|
||||
|
||||
// Condition checks shall still be run even if macro is paused.
|
||||
// Otherwise conditions might behave in unexpected ways when resuming.
|
||||
//
|
||||
// For example, audio could immediately match after unpause, when it
|
||||
// matched before it was paused due to timers not being updated.
|
||||
if (_paused) {
|
||||
vblog(LOG_INFO, "Macro %s is paused", _name.c_str());
|
||||
_matched = false;
|
||||
return false;
|
||||
// TODO: Move back to PerformAction() once new scene collection frontend
|
||||
// events are available - see:
|
||||
// https://github.com/obsproject/obs-studio/commit/feda1aaa283e8a99f6ba1159cfe6b9c1f2934a61
|
||||
if (_matched && _count != std::numeric_limits<int>::max()) {
|
||||
_count++;
|
||||
}
|
||||
|
||||
return _matched;
|
||||
@@ -113,9 +114,6 @@ bool Macro::PerformAction()
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (_count != std::numeric_limits<int>::max()) {
|
||||
_count++;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -125,6 +123,21 @@ void Macro::SetName(const std::string &name)
|
||||
SetHotkeysDesc();
|
||||
}
|
||||
|
||||
void Macro::ResetTimers()
|
||||
{
|
||||
for (auto &c : _conditions) {
|
||||
c->ResetDuration();
|
||||
}
|
||||
}
|
||||
|
||||
void Macro::SetPaused(bool pause)
|
||||
{
|
||||
if (_paused && !pause) {
|
||||
ResetTimers();
|
||||
}
|
||||
_paused = pause;
|
||||
}
|
||||
|
||||
void Macro::UpdateActionIndices()
|
||||
{
|
||||
int idx = 0;
|
||||
@@ -290,13 +303,18 @@ void Macro::ResolveMacroRef()
|
||||
MacroRefCondition *ref =
|
||||
dynamic_cast<MacroRefCondition *>(c.get());
|
||||
if (ref) {
|
||||
ref->_macro.UpdateRef();
|
||||
ref->ResolveMacroRef();
|
||||
}
|
||||
}
|
||||
for (auto &a : _actions) {
|
||||
MacroRefAction *ref = dynamic_cast<MacroRefAction *>(a.get());
|
||||
if (ref) {
|
||||
ref->_macro.UpdateRef();
|
||||
ref->ResolveMacroRef();
|
||||
}
|
||||
MultiMacroRefAction *ref2 =
|
||||
dynamic_cast<MultiMacroRefAction *>(a.get());
|
||||
if (ref2) {
|
||||
ref2->ResolveMacroRef();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -445,7 +463,7 @@ void SwitcherData::saveMacros(obs_data_t *obj)
|
||||
for (auto &m : macros) {
|
||||
obs_data_t *array_obj = obs_data_create();
|
||||
|
||||
m.Save(array_obj);
|
||||
m->Save(array_obj);
|
||||
obs_data_array_push_back(macroArray, array_obj);
|
||||
|
||||
obs_data_release(array_obj);
|
||||
@@ -463,14 +481,14 @@ void SwitcherData::loadMacros(obs_data_t *obj)
|
||||
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
obs_data_t *array_obj = obs_data_array_item(macroArray, i);
|
||||
macros.emplace_back();
|
||||
macros.back().Load(array_obj);
|
||||
macros.emplace_back(std::make_shared<Macro>());
|
||||
macros.back()->Load(array_obj);
|
||||
obs_data_release(array_obj);
|
||||
}
|
||||
obs_data_array_release(macroArray);
|
||||
|
||||
for (auto &m : macros) {
|
||||
m.ResolveMacroRef();
|
||||
m->ResolveMacroRef();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -478,11 +496,11 @@ bool SwitcherData::checkMacros()
|
||||
{
|
||||
bool ret = false;
|
||||
for (auto &m : macros) {
|
||||
if (m.CeckMatch()) {
|
||||
if (m->CeckMatch()) {
|
||||
ret = true;
|
||||
// This has to be performed here for now as actions are
|
||||
// not performed immediately after checking conditions.
|
||||
if (m.SwitchesScene()) {
|
||||
if (m->SwitchesScene()) {
|
||||
switcher->macroSceneSwitched = true;
|
||||
}
|
||||
}
|
||||
@@ -492,12 +510,15 @@ bool SwitcherData::checkMacros()
|
||||
|
||||
bool SwitcherData::runMacros()
|
||||
{
|
||||
// TODO: Don't rely on creating a copy of each macro once new frontend
|
||||
// events are available - see:
|
||||
// https://github.com/obsproject/obs-studio/commit/feda1aaa283e8a99f6ba1159cfe6b9c1f2934a61
|
||||
for (auto m : macros) {
|
||||
if (m.Matched()) {
|
||||
vblog(LOG_INFO, "running macro: %s", m.Name().c_str());
|
||||
if (!m.PerformAction()) {
|
||||
if (m->Matched()) {
|
||||
vblog(LOG_INFO, "running macro: %s", m->Name().c_str());
|
||||
if (!m->PerformAction()) {
|
||||
blog(LOG_WARNING, "abort macro: %s",
|
||||
m.Name().c_str());
|
||||
m->Name().c_str());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -508,8 +529,8 @@ bool SwitcherData::runMacros()
|
||||
Macro *GetMacroByName(const char *name)
|
||||
{
|
||||
for (auto &m : switcher->macros) {
|
||||
if (m.Name() == name) {
|
||||
return &m;
|
||||
if (m->Name() == name) {
|
||||
return m.get();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -570,3 +591,10 @@ void MacroRefAction::ResolveMacroRef()
|
||||
{
|
||||
_macro.UpdateRef();
|
||||
}
|
||||
|
||||
void MultiMacroRefAction::ResolveMacroRef()
|
||||
{
|
||||
for (auto &m : _macros) {
|
||||
m.UpdateRef();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -229,6 +229,16 @@ void clearLayout(QLayout *layout, int afterIdx)
|
||||
}
|
||||
}
|
||||
|
||||
void setLayoutVisible(QLayout *layout, bool visible)
|
||||
{
|
||||
for (int i = 0; i < layout->count(); ++i) {
|
||||
QWidget *widget = layout->itemAt(i)->widget();
|
||||
if (widget != NULL) {
|
||||
widget->setVisible(visible);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool compareIgnoringLineEnding(QString &s1, QString &s2)
|
||||
{
|
||||
// Let QT deal with different types of lineendings
|
||||
@@ -800,7 +810,8 @@ void populateSceneItemSelection(QComboBox *list, SceneSelection &s)
|
||||
{
|
||||
std::set<QString> names;
|
||||
|
||||
if (s.GetType() == SceneSelectionType::CURRENT) {
|
||||
if (s.GetType() == SceneSelectionType::CURRENT ||
|
||||
s.GetType() == SceneSelectionType::PREVIOUS) {
|
||||
auto enumScenes = [](void *param, obs_source_t *source) {
|
||||
if (!source) {
|
||||
return true;
|
||||
@@ -828,6 +839,36 @@ void populateSceneItemSelection(QComboBox *list, SceneSelection &s)
|
||||
list->setCurrentIndex(0);
|
||||
}
|
||||
|
||||
void populateSourceTypeSelection(QComboBox *list)
|
||||
{
|
||||
std::set<QString> sourceTypeNames;
|
||||
auto enumSourcesTypes = [](void *param, obs_source_t *source) {
|
||||
if (!source) {
|
||||
return true;
|
||||
}
|
||||
std::set<QString> *names =
|
||||
reinterpret_cast<std::set<QString> *>(param);
|
||||
if (obs_source_get_type(source) != OBS_SOURCE_TYPE_INPUT) {
|
||||
return true;
|
||||
}
|
||||
names->insert(
|
||||
obs_source_get_display_name(obs_source_get_id(source)));
|
||||
return true;
|
||||
};
|
||||
|
||||
obs_enum_sources(enumSourcesTypes, &sourceTypeNames);
|
||||
|
||||
for (auto &name : sourceTypeNames) {
|
||||
if (!name.isEmpty()) {
|
||||
list->addItem(name);
|
||||
}
|
||||
}
|
||||
|
||||
list->model()->sort(0);
|
||||
addSelectionEntry(list, obs_module_text("AdvSceneSwitcher.selectItem"));
|
||||
list->setCurrentIndex(0);
|
||||
}
|
||||
|
||||
QMetaObject::Connection PulseWidget(QWidget *widget, QColor startColor,
|
||||
QColor endColor, QString specifier,
|
||||
bool once)
|
||||
|
||||
Reference in New Issue
Block a user