mirror of
https://github.com/WarmUpTill/SceneSwitcher.git
synced 2026-04-13 13:16:46 -05:00
Add option to extend scene sequences (#121)
Extending a scene sequence allows for more complex switching setups, but should also simplify some setups. Sequences like "A -> B -> A -> C", which previously were only possible by creating a copy of A, can now be specified in a single entry. To extend a sequence either select the sequence you want to modify and click the extend sequence button or simply double click the entry.
This commit is contained in:
parent
920c188213
commit
7947ad2608
|
|
@ -186,6 +186,8 @@ AdvSceneSwitcher.sceneSequenceTab.fileType="Text files (*.txt)"
|
|||
AdvSceneSwitcher.sceneSequenceTab.interruptible="interruptible"
|
||||
AdvSceneSwitcher.sceneSequenceTab.interruptibleHint="Other switching methods are allowed to interrupt this scene sequence"
|
||||
AdvSceneSwitcher.sceneSequenceTab.entry="When {{startScenes}} is active switch to {{scenes}} after {{delay}} {{delayUnits}} using {{transitions}} {{interruptible}}"
|
||||
AdvSceneSwitcher.sceneSequenceTab.extendEdit="Extend Sequence"
|
||||
AdvSceneSwitcher.sceneSequenceTab.extendEntry="After {{delay}} {{delayUnits}} switch to {{scenes}} using {{transitions}}"
|
||||
AdvSceneSwitcher.sceneSequenceTab.help="This tab will allow you to automatically switch to a different scene if a scene was active for a configured period of time.\nFor example, you could automatically cycle back and forth between two scenes automatically.\n\nClick on the highlighted plus symbol to continue."
|
||||
|
||||
; Audio Tab
|
||||
|
|
|
|||
|
|
@ -2824,6 +2824,20 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="line">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="sequenceEdit">
|
||||
<property name="text">
|
||||
<string>AdvSceneSwitcher.sceneSequenceTab.extendEdit</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_8">
|
||||
<property name="orientation">
|
||||
|
|
|
|||
|
|
@ -469,61 +469,17 @@ void SwitcherData::Thread()
|
|||
linger = 0;
|
||||
|
||||
switcher->Prune();
|
||||
|
||||
if (switcher->stop) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (checkPause()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int switchFuncName : functionNamesByPriority) {
|
||||
switch (switchFuncName) {
|
||||
case read_file_func:
|
||||
checkSwitchInfoFromFile(match, scene,
|
||||
transition);
|
||||
checkFileContent(match, scene, transition);
|
||||
break;
|
||||
case idle_func:
|
||||
checkIdleSwitch(match, scene, transition);
|
||||
break;
|
||||
case exe_func:
|
||||
checkExeSwitch(match, scene, transition);
|
||||
break;
|
||||
case screen_region_func:
|
||||
checkScreenRegionSwitch(match, scene,
|
||||
transition);
|
||||
break;
|
||||
case window_title_func:
|
||||
checkWindowTitleSwitch(match, scene,
|
||||
transition);
|
||||
break;
|
||||
case round_trip_func:
|
||||
checkSceneSequence(match, scene, transition,
|
||||
linger);
|
||||
break;
|
||||
case media_func:
|
||||
checkMediaSwitch(match, scene, transition);
|
||||
break;
|
||||
case time_func:
|
||||
checkTimeSwitch(match, scene, transition);
|
||||
break;
|
||||
case audio_func:
|
||||
checkAudioSwitch(match, scene, transition);
|
||||
break;
|
||||
}
|
||||
|
||||
if (switcher->stop) {
|
||||
goto endLoop;
|
||||
}
|
||||
if (match) {
|
||||
break;
|
||||
}
|
||||
match = checkForMatch(scene, transition, linger);
|
||||
if (switcher->stop) {
|
||||
break;
|
||||
}
|
||||
|
||||
checkNoMatchSwitch(match, scene, transition, sleep);
|
||||
|
||||
checkSwitchCooldown(match);
|
||||
|
||||
if (linger) {
|
||||
|
|
@ -561,10 +517,64 @@ void SwitcherData::Thread()
|
|||
|
||||
writeSceneInfoToFile();
|
||||
}
|
||||
endLoop:
|
||||
|
||||
blog(LOG_INFO, "stopped");
|
||||
}
|
||||
|
||||
bool SwitcherData::checkForMatch(OBSWeakSource &scene,
|
||||
OBSWeakSource &transition, int &linger)
|
||||
{
|
||||
bool match = false;
|
||||
|
||||
if (uninterruptibleSceneSequenceActive) {
|
||||
checkSceneSequence(match, scene, transition, linger);
|
||||
if (match) {
|
||||
return match;
|
||||
}
|
||||
}
|
||||
|
||||
for (int switchFuncName : functionNamesByPriority) {
|
||||
switch (switchFuncName) {
|
||||
case read_file_func:
|
||||
checkSwitchInfoFromFile(match, scene, transition);
|
||||
checkFileContent(match, scene, transition);
|
||||
break;
|
||||
case idle_func:
|
||||
checkIdleSwitch(match, scene, transition);
|
||||
break;
|
||||
case exe_func:
|
||||
checkExeSwitch(match, scene, transition);
|
||||
break;
|
||||
case screen_region_func:
|
||||
checkScreenRegionSwitch(match, scene, transition);
|
||||
break;
|
||||
case window_title_func:
|
||||
checkWindowTitleSwitch(match, scene, transition);
|
||||
break;
|
||||
case round_trip_func:
|
||||
checkSceneSequence(match, scene, transition, linger);
|
||||
break;
|
||||
case media_func:
|
||||
checkMediaSwitch(match, scene, transition);
|
||||
break;
|
||||
case time_func:
|
||||
checkTimeSwitch(match, scene, transition);
|
||||
break;
|
||||
case audio_func:
|
||||
checkAudioSwitch(match, scene, transition);
|
||||
break;
|
||||
}
|
||||
|
||||
if (switcher->stop) {
|
||||
return false;
|
||||
}
|
||||
if (match) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return match;
|
||||
}
|
||||
|
||||
void switchScene(OBSWeakSource &scene, OBSWeakSource &transition,
|
||||
bool &transitionOverrideOverride)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@ public:
|
|||
int IgnoreIdleWindowsFindByData(const QString &window);
|
||||
|
||||
void UpdateNonMatchingScene(const QString &name);
|
||||
void OpenSequenceExtendEdit(SequenceWidget *sw);
|
||||
void SetEditSceneGroup(SceneGroup &sg);
|
||||
|
||||
void loadUI();
|
||||
|
|
@ -136,10 +137,12 @@ public slots:
|
|||
|
||||
void on_sceneSequenceAdd_clicked();
|
||||
void on_sceneSequenceRemove_clicked();
|
||||
void on_sceneSequenceSave_clicked();
|
||||
void on_sceneSequenceLoad_clicked();
|
||||
void on_sceneSequenceUp_clicked();
|
||||
void on_sceneSequenceDown_clicked();
|
||||
void on_sceneSequenceSave_clicked();
|
||||
void on_sceneSequenceLoad_clicked();
|
||||
void on_sequenceEdit_clicked();
|
||||
void on_sceneSequenceSwitches_itemDoubleClicked(QListWidgetItem *item);
|
||||
|
||||
void on_verboseLogging_stateChanged(int state);
|
||||
void on_uiHintsDisable_stateChanged(int state);
|
||||
|
|
|
|||
|
|
@ -73,7 +73,7 @@ public:
|
|||
|
||||
static void swapSwitchData(SwitchWidget *s1, SwitchWidget *s2);
|
||||
|
||||
private slots:
|
||||
protected slots:
|
||||
void SceneChanged(const QString &text);
|
||||
void TransitionChanged(const QString &text);
|
||||
void SceneGroupAdd(const QString &name);
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
#pragma once
|
||||
#include <QVBoxLayout>
|
||||
|
||||
#include "switch-generic.hpp"
|
||||
|
||||
constexpr auto round_trip_func = 1;
|
||||
|
|
@ -12,42 +14,77 @@ typedef enum {
|
|||
|
||||
struct SceneSequenceSwitch : SceneSwitcherEntry {
|
||||
static bool pause;
|
||||
SwitchTargetType startTargetType = SwitchTargetType::Scene;
|
||||
OBSWeakSource startScene = nullptr;
|
||||
double delay = 0;
|
||||
int delayMultiplier = 1;
|
||||
bool interruptible = false;
|
||||
unsigned int matchCount = 0;
|
||||
|
||||
// nullptr marks start point and reaching end of extended sequence
|
||||
SceneSequenceSwitch *activeSequence = nullptr;
|
||||
|
||||
std::unique_ptr<SceneSequenceSwitch> extendedSequence = nullptr;
|
||||
|
||||
const char *getType() { return "sequence"; }
|
||||
bool initialized();
|
||||
bool valid();
|
||||
void save(obs_data_t *obj);
|
||||
void load(obs_data_t *obj);
|
||||
void save(obs_data_t *obj, bool saveExt = true);
|
||||
void load(obs_data_t *obj, bool saveExt = true);
|
||||
|
||||
bool reduce();
|
||||
SceneSequenceSwitch *extend();
|
||||
|
||||
bool checkMatch(OBSWeakSource currentScene, int &linger,
|
||||
SceneSequenceSwitch *root = nullptr);
|
||||
bool checkDurationMatchInterruptible();
|
||||
void prepareUninterruptibleMatch(OBSWeakSource currentScene,
|
||||
int &linger);
|
||||
void advanceActiveSequence();
|
||||
void logAdvanceSequence();
|
||||
void logSequenceCanceled();
|
||||
};
|
||||
|
||||
class SequenceWidget : public SwitchWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
SequenceWidget(QWidget *parent, SceneSequenceSwitch *s);
|
||||
SequenceWidget(QWidget *parent, SceneSequenceSwitch *s,
|
||||
bool extendSequence = false,
|
||||
bool editExtendMode = false);
|
||||
SceneSequenceSwitch *getSwitchData();
|
||||
void setSwitchData(SceneSequenceSwitch *s);
|
||||
|
||||
static void swapSwitchData(SequenceWidget *s1, SequenceWidget *s2);
|
||||
|
||||
void UpdateDelay();
|
||||
void UpdateExtendText();
|
||||
void setExtendedSequenceStartScene();
|
||||
|
||||
private slots:
|
||||
void SceneChanged(const QString &text);
|
||||
void DelayChanged(double delay);
|
||||
void DelayUnitsChanged(int idx);
|
||||
void StartSceneChanged(const QString &text);
|
||||
void InterruptibleChanged(int state);
|
||||
void ExtendClicked();
|
||||
void ReduceClicked();
|
||||
|
||||
private:
|
||||
protected:
|
||||
QDoubleSpinBox *delay;
|
||||
QComboBox *delayUnits;
|
||||
QComboBox *startScenes;
|
||||
QCheckBox *interruptible;
|
||||
QVBoxLayout *extendSequenceLayout;
|
||||
QPushButton *extend;
|
||||
QPushButton *reduce;
|
||||
|
||||
// I would prefer having a list of only widgets of type editExtendMode
|
||||
// but I am not sure how to best implement that using a QListWidget.
|
||||
//
|
||||
// So use edit button to bring up edit widget and
|
||||
// add a label to disaplay current extended sequence state.
|
||||
QLabel *extendText;
|
||||
|
||||
SceneSequenceSwitch *switchData;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -85,6 +85,7 @@ struct SwitcherData {
|
|||
|
||||
std::vector<std::string> ignoreWindowsSwitches;
|
||||
|
||||
bool uninterruptibleSceneSequenceActive = false;
|
||||
std::deque<SceneSequenceSwitch> sceneSequenceSwitches;
|
||||
|
||||
std::deque<RandomSwitch> randomSwitches;
|
||||
|
|
@ -160,11 +161,14 @@ struct SwitcherData {
|
|||
void Stop();
|
||||
|
||||
bool sceneChangedDuringWait();
|
||||
|
||||
bool prioFuncsValid();
|
||||
|
||||
void writeSceneInfoToFile();
|
||||
void writeToStatusFile(QString status);
|
||||
bool checkPause();
|
||||
void checkDefaultSceneTransitions();
|
||||
|
||||
bool checkForMatch(OBSWeakSource &scene, OBSWeakSource &transition,
|
||||
int &linger);
|
||||
void checkSceneSequence(bool &match, OBSWeakSource &scene,
|
||||
OBSWeakSource &transition, int &linger);
|
||||
void checkIdleSwitch(bool &match, OBSWeakSource &scene,
|
||||
|
|
@ -193,6 +197,8 @@ struct SwitcherData {
|
|||
OBSWeakSource &transition, int &sleep);
|
||||
void checkSwitchCooldown(bool &match);
|
||||
void checkTriggers();
|
||||
bool checkPause();
|
||||
void checkDefaultSceneTransitions();
|
||||
|
||||
void saveSettings(obs_data_t *obj);
|
||||
void saveWindowTitleSwitches(obs_data_t *obj);
|
||||
|
|
|
|||
|
|
@ -4,6 +4,8 @@
|
|||
#include "headers/advanced-scene-switcher.hpp"
|
||||
#include "headers/utility.hpp"
|
||||
|
||||
constexpr auto max_extend_text_size = 150;
|
||||
|
||||
bool SceneSequenceSwitch::pause = false;
|
||||
static QMetaObject::Connection addPulse;
|
||||
|
||||
|
|
@ -159,30 +161,41 @@ void AdvSceneSwitcher::on_sceneSequenceLoad_clicked()
|
|||
close();
|
||||
}
|
||||
|
||||
bool matchInterruptible(SwitcherData *switcher, SceneSequenceSwitch &s)
|
||||
void AdvSceneSwitcher::OpenSequenceExtendEdit(SequenceWidget *sw)
|
||||
{
|
||||
bool durationReached = s.matchCount * (switcher->interval / 1000.0) >=
|
||||
s.delay;
|
||||
QDialog edit;
|
||||
SequenceWidget editWidget(this, sw->getSwitchData(), false, true);
|
||||
QHBoxLayout layout;
|
||||
layout.setSizeConstraint(QLayout::SetFixedSize);
|
||||
layout.addWidget(&editWidget);
|
||||
edit.setLayout(&layout);
|
||||
edit.setWindowTitle(obs_module_text(
|
||||
"AdvSceneSwitcher.sceneSequenceTab.extendEdit"));
|
||||
edit.exec();
|
||||
|
||||
s.matchCount++;
|
||||
|
||||
if (durationReached) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
sw->UpdateExtendText();
|
||||
}
|
||||
|
||||
bool matchUninterruptible(SwitcherData *switcher, SceneSequenceSwitch &s,
|
||||
obs_source_t *currentSource, int &linger)
|
||||
void AdvSceneSwitcher::on_sequenceEdit_clicked()
|
||||
{
|
||||
// scene was already active for the previous cycle so remove this time
|
||||
int dur = s.delay * 1000 - switcher->interval;
|
||||
if (dur > 0) {
|
||||
switcher->waitScene = currentSource;
|
||||
linger = dur;
|
||||
int index = ui->sceneSequenceSwitches->currentRow();
|
||||
if (index == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
return true;
|
||||
SequenceWidget *currentWidget =
|
||||
(SequenceWidget *)ui->sceneSequenceSwitches->itemWidget(
|
||||
ui->sceneSequenceSwitches->item(index));
|
||||
|
||||
OpenSequenceExtendEdit(currentWidget);
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::on_sceneSequenceSwitches_itemDoubleClicked(
|
||||
QListWidgetItem *item)
|
||||
{
|
||||
SequenceWidget *currentWidget =
|
||||
(SequenceWidget *)ui->sceneSequenceSwitches->itemWidget(item);
|
||||
OpenSequenceExtendEdit(currentWidget);
|
||||
}
|
||||
|
||||
void SwitcherData::checkSceneSequence(bool &match, OBSWeakSource &scene,
|
||||
|
|
@ -192,38 +205,53 @@ void SwitcherData::checkSceneSequence(bool &match, OBSWeakSource &scene,
|
|||
return;
|
||||
}
|
||||
|
||||
obs_source_t *currentSource = obs_frontend_get_current_scene();
|
||||
obs_weak_source_t *ws = obs_source_get_weak_source(currentSource);
|
||||
obs_source_t *currentSceneSource = obs_frontend_get_current_scene();
|
||||
obs_weak_source_t *currentScene =
|
||||
obs_source_get_weak_source(currentSceneSource);
|
||||
|
||||
for (SceneSequenceSwitch &s : sceneSequenceSwitches) {
|
||||
if (!s.initialized()) {
|
||||
// Continue the active uninterruptible sequence and skip others
|
||||
if (uninterruptibleSceneSequenceActive &&
|
||||
s.activeSequence == nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (s.startScene == ws) {
|
||||
if (!match) {
|
||||
if (s.interruptible) {
|
||||
match = matchInterruptible(switcher, s);
|
||||
} else {
|
||||
match = matchUninterruptible(
|
||||
switcher, s, currentSource,
|
||||
linger);
|
||||
}
|
||||
bool matched = s.checkMatch(currentScene, linger);
|
||||
|
||||
if (match) {
|
||||
scene = s.getScene();
|
||||
transition = s.transition;
|
||||
if (switcher->verbose) {
|
||||
s.logMatch();
|
||||
}
|
||||
if (!match && matched) {
|
||||
match = matched;
|
||||
|
||||
if (s.activeSequence) {
|
||||
scene = s.activeSequence->getScene();
|
||||
transition = s.activeSequence->transition;
|
||||
} else {
|
||||
scene = s.getScene();
|
||||
transition = s.transition;
|
||||
if (verbose) {
|
||||
s.logMatch();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
s.matchCount = 0;
|
||||
|
||||
s.advanceActiveSequence();
|
||||
if (verbose) {
|
||||
s.logAdvanceSequence();
|
||||
}
|
||||
|
||||
// Ignore other switching methods if sequence is not
|
||||
// interruptible and has not reached its end
|
||||
if (s.activeSequence) {
|
||||
uninterruptibleSceneSequenceActive =
|
||||
!s.interruptible;
|
||||
}
|
||||
}
|
||||
}
|
||||
obs_source_release(currentSource);
|
||||
obs_weak_source_release(ws);
|
||||
|
||||
if (!match) {
|
||||
uninterruptibleSceneSequenceActive = false;
|
||||
}
|
||||
|
||||
obs_source_release(currentSceneSource);
|
||||
obs_weak_source_release(currentScene);
|
||||
}
|
||||
|
||||
void SwitcherData::saveSceneSequenceSwitches(obs_data_t *obj)
|
||||
|
|
@ -291,18 +319,32 @@ bool SceneSequenceSwitch::valid()
|
|||
(SceneSwitcherEntry::valid() && WeakSourceValid(startScene));
|
||||
}
|
||||
|
||||
void SceneSequenceSwitch::save(obs_data_t *obj)
|
||||
void SceneSequenceSwitch::save(obs_data_t *obj, bool saveExt)
|
||||
{
|
||||
SceneSwitcherEntry::save(obj);
|
||||
|
||||
obs_data_set_int(obj, "startTargetType",
|
||||
static_cast<int>(startTargetType));
|
||||
obs_data_set_string(obj, "startScene",
|
||||
GetWeakSourceName(startScene).c_str());
|
||||
|
||||
obs_data_set_double(obj, "delay", delay);
|
||||
|
||||
obs_data_set_int(obj, "delayMultiplier", delayMultiplier);
|
||||
|
||||
obs_data_set_bool(obj, "interruptible", interruptible);
|
||||
|
||||
if (saveExt) {
|
||||
auto cur = extendedSequence.get();
|
||||
|
||||
obs_data_array_t *extendScenes = obs_data_array_create();
|
||||
while (cur) {
|
||||
obs_data_t *array_obj = obs_data_create();
|
||||
cur->save(array_obj, false);
|
||||
obs_data_array_push_back(extendScenes, array_obj);
|
||||
obs_data_release(array_obj);
|
||||
cur = cur->extendedSequence.get();
|
||||
}
|
||||
obs_data_set_array(obj, "extendScenes", extendScenes);
|
||||
obs_data_array_release(extendScenes);
|
||||
}
|
||||
}
|
||||
|
||||
// To be removed in future version
|
||||
|
|
@ -341,14 +383,15 @@ bool loadOldScequence(obs_data_t *obj, SceneSequenceSwitch *s)
|
|||
return true;
|
||||
}
|
||||
|
||||
void SceneSequenceSwitch::load(obs_data_t *obj)
|
||||
void SceneSequenceSwitch::load(obs_data_t *obj, bool saveExt)
|
||||
{
|
||||
if (loadOldScequence(obj, this)) {
|
||||
return;
|
||||
}
|
||||
|
||||
SceneSwitcherEntry::load(obj);
|
||||
|
||||
startTargetType = static_cast<SwitchTargetType>(
|
||||
obs_data_get_int(obj, "startTargetType"));
|
||||
const char *scene = obs_data_get_string(obj, "startScene");
|
||||
startScene = GetWeakSourceByName(scene);
|
||||
|
||||
|
|
@ -360,6 +403,173 @@ void SceneSequenceSwitch::load(obs_data_t *obj)
|
|||
delayMultiplier = 1;
|
||||
|
||||
interruptible = obs_data_get_bool(obj, "interruptible");
|
||||
|
||||
if (saveExt) {
|
||||
auto cur = this;
|
||||
|
||||
obs_data_array_t *extendScenes =
|
||||
obs_data_get_array(obj, "extendScenes");
|
||||
size_t count = obs_data_array_count(extendScenes);
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
obs_data_t *array_obj =
|
||||
obs_data_array_item(extendScenes, i);
|
||||
|
||||
cur->extendedSequence =
|
||||
std::make_unique<SceneSequenceSwitch>();
|
||||
|
||||
cur->extendedSequence->load(array_obj, false);
|
||||
|
||||
cur = cur->extendedSequence.get();
|
||||
obs_data_release(array_obj);
|
||||
}
|
||||
obs_data_array_release(extendScenes);
|
||||
}
|
||||
}
|
||||
|
||||
bool SceneSequenceSwitch::reduce()
|
||||
{
|
||||
// Reset activeSequence just in case it was one of the deleted entries
|
||||
activeSequence = nullptr;
|
||||
|
||||
if (!extendedSequence) {
|
||||
return true;
|
||||
}
|
||||
if (extendedSequence->reduce()) {
|
||||
extendedSequence.reset(nullptr);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
SceneSequenceSwitch *SceneSequenceSwitch::extend()
|
||||
{
|
||||
if (extendedSequence) {
|
||||
return extendedSequence->extend();
|
||||
}
|
||||
extendedSequence = std::make_unique<SceneSequenceSwitch>();
|
||||
extendedSequence->startScene = scene;
|
||||
return extendedSequence.get();
|
||||
}
|
||||
|
||||
bool SceneSequenceSwitch::checkMatch(OBSWeakSource currentScene, int &linger,
|
||||
SceneSequenceSwitch *root)
|
||||
{
|
||||
if (!initialized()) {
|
||||
if (root) {
|
||||
root->activeSequence = nullptr;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool match = false;
|
||||
|
||||
if (activeSequence) {
|
||||
return activeSequence->checkMatch(currentScene, linger, this);
|
||||
}
|
||||
|
||||
if (startScene == currentScene) {
|
||||
if (interruptible) {
|
||||
match = checkDurationMatchInterruptible();
|
||||
} else {
|
||||
match = true;
|
||||
prepareUninterruptibleMatch(currentScene, linger);
|
||||
}
|
||||
} else {
|
||||
matchCount = 0;
|
||||
|
||||
if (root) {
|
||||
root->activeSequence = nullptr;
|
||||
logSequenceCanceled();
|
||||
}
|
||||
}
|
||||
|
||||
return match;
|
||||
}
|
||||
|
||||
bool SceneSequenceSwitch::checkDurationMatchInterruptible()
|
||||
{
|
||||
bool durationReached = matchCount * (switcher->interval / 1000.0) >=
|
||||
delay;
|
||||
matchCount++;
|
||||
if (durationReached) {
|
||||
matchCount = 0;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void SceneSequenceSwitch::prepareUninterruptibleMatch(
|
||||
OBSWeakSource currentScene, int &linger)
|
||||
{
|
||||
int dur = delay * 1000;
|
||||
if (dur > 0) {
|
||||
switcher->waitScene = obs_weak_source_get_source(currentScene);
|
||||
obs_source_release(switcher->waitScene);
|
||||
linger = dur;
|
||||
}
|
||||
}
|
||||
|
||||
void SceneSequenceSwitch::advanceActiveSequence()
|
||||
{
|
||||
// Set start Scene
|
||||
OBSWeakSource currentSceneGroupScene = nullptr;
|
||||
if (targetType == SwitchTargetType::SceneGroup && group) {
|
||||
currentSceneGroupScene = group->getCurrentScene();
|
||||
}
|
||||
|
||||
if (activeSequence) {
|
||||
activeSequence = activeSequence->extendedSequence.get();
|
||||
} else {
|
||||
activeSequence = extendedSequence.get();
|
||||
}
|
||||
|
||||
if (activeSequence) {
|
||||
if (activeSequence->startTargetType ==
|
||||
SwitchTargetType::SceneGroup) {
|
||||
activeSequence->startScene = currentSceneGroupScene;
|
||||
}
|
||||
if (activeSequence->targetType == SwitchTargetType::Scene &&
|
||||
!activeSequence->scene) {
|
||||
blog(LOG_WARNING,
|
||||
"cannot advance sequence - null scene set");
|
||||
activeSequence = nullptr;
|
||||
}
|
||||
if (activeSequence->targetType ==
|
||||
SwitchTargetType::SceneGroup &&
|
||||
activeSequence->group &&
|
||||
activeSequence->group->scenes.empty()) {
|
||||
blog(LOG_WARNING,
|
||||
"cannot advance sequence - no scenes specified in '%s'",
|
||||
activeSequence->group->name.c_str());
|
||||
activeSequence = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
// Reinit old matchCount value in case it was previously set
|
||||
activeSequence->matchCount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void SceneSequenceSwitch::logAdvanceSequence()
|
||||
{
|
||||
if (activeSequence) {
|
||||
std::string targetName =
|
||||
GetWeakSourceName(activeSequence->scene);
|
||||
|
||||
if (activeSequence->targetType ==
|
||||
SwitchTargetType::SceneGroup &&
|
||||
activeSequence->group) {
|
||||
targetName = activeSequence->group->name;
|
||||
}
|
||||
|
||||
blog(LOG_INFO, "continuing sequence with '%s' -> '%s'",
|
||||
GetWeakSourceName(activeSequence->startScene).c_str(),
|
||||
targetName.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void SceneSequenceSwitch::logSequenceCanceled()
|
||||
{
|
||||
blog(LOG_INFO, "unexpected scene change - cancel sequence");
|
||||
}
|
||||
|
||||
void populateDelayUnits(QComboBox *list)
|
||||
|
|
@ -369,14 +579,82 @@ void populateDelayUnits(QComboBox *list)
|
|||
list->addItem(obs_module_text("AdvSceneSwitcher.unit.hours"));
|
||||
}
|
||||
|
||||
SequenceWidget::SequenceWidget(QWidget *parent, SceneSequenceSwitch *s)
|
||||
: SwitchWidget(parent, s, true, true)
|
||||
QString makeExtendText(SceneSequenceSwitch *s, int curLen = 0)
|
||||
{
|
||||
if (!s) {
|
||||
return "";
|
||||
}
|
||||
|
||||
QString ext = "";
|
||||
|
||||
ext = QString::number(s->delay / s->delayMultiplier) + " ";
|
||||
|
||||
switch (s->delayMultiplier) {
|
||||
case 1:
|
||||
ext += obs_module_text("AdvSceneSwitcher.unit.secends");
|
||||
break;
|
||||
case 60:
|
||||
ext += obs_module_text("AdvSceneSwitcher.unit.minutes");
|
||||
break;
|
||||
case 60 * 60:
|
||||
ext += obs_module_text("AdvSceneSwitcher.unit.hours");
|
||||
break;
|
||||
default:
|
||||
ext += obs_module_text("?????");
|
||||
break;
|
||||
}
|
||||
|
||||
QString sceneName = GetWeakSourceName(s->scene).c_str();
|
||||
if (s->targetType == SwitchTargetType::SceneGroup && s->group) {
|
||||
sceneName = QString::fromStdString(s->group->name);
|
||||
}
|
||||
if (sceneName.isEmpty()) {
|
||||
sceneName = obs_module_text("AdvSceneSwitcher.selectScene");
|
||||
}
|
||||
ext += " -> [" + sceneName + "]";
|
||||
|
||||
if (ext.length() + curLen > max_extend_text_size) {
|
||||
return "...";
|
||||
}
|
||||
|
||||
if (s->extendedSequence.get()) {
|
||||
return ext +=
|
||||
" | " + makeExtendText(s->extendedSequence.get(),
|
||||
curLen + ext.length());
|
||||
} else {
|
||||
return ext;
|
||||
}
|
||||
}
|
||||
|
||||
SequenceWidget::SequenceWidget(QWidget *parent, SceneSequenceSwitch *s,
|
||||
bool extendSequence, bool editExtendMode)
|
||||
: SwitchWidget(parent, s, !extendSequence, true)
|
||||
{
|
||||
this->setParent(parent);
|
||||
|
||||
delay = new QDoubleSpinBox();
|
||||
delayUnits = new QComboBox();
|
||||
startScenes = new QComboBox();
|
||||
interruptible = new QCheckBox(obs_module_text(
|
||||
"AdvSceneSwitcher.sceneSequenceTab.interruptible"));
|
||||
extendText = new QLabel();
|
||||
extend = new QPushButton();
|
||||
reduce = new QPushButton();
|
||||
|
||||
extend->setProperty("themeID",
|
||||
QVariant(QStringLiteral("addIconSmall")));
|
||||
reduce->setProperty("themeID",
|
||||
QVariant(QStringLiteral("removeIconSmall")));
|
||||
|
||||
extend->setMaximumSize(22, 22);
|
||||
reduce->setMaximumSize(22, 22);
|
||||
|
||||
// We need to extend the generic SwitchWidget::SceneChanged()
|
||||
// with our own SequenceWidget::SceneChanged()
|
||||
// so the old singal / slot needs to be disconnected
|
||||
scenes->disconnect();
|
||||
QWidget::connect(scenes, SIGNAL(currentTextChanged(const QString &)),
|
||||
this, SLOT(SceneChanged(const QString &)));
|
||||
|
||||
QWidget::connect(delay, SIGNAL(valueChanged(double)), this,
|
||||
SLOT(DelayChanged(double)));
|
||||
|
|
@ -388,6 +666,11 @@ SequenceWidget::SequenceWidget(QWidget *parent, SceneSequenceSwitch *s)
|
|||
QWidget::connect(interruptible, SIGNAL(stateChanged(int)), this,
|
||||
SLOT(InterruptibleChanged(int)));
|
||||
|
||||
QWidget::connect(extend, SIGNAL(clicked()), this,
|
||||
SLOT(ExtendClicked()));
|
||||
QWidget::connect(reduce, SIGNAL(clicked()), this,
|
||||
SLOT(ReduceClicked()));
|
||||
|
||||
delay->setMaximum(99999.000000);
|
||||
AdvSceneSwitcher::populateSceneSelection(startScenes, false);
|
||||
populateDelayUnits(delayUnits);
|
||||
|
|
@ -413,17 +696,62 @@ SequenceWidget::SequenceWidget(QWidget *parent, SceneSequenceSwitch *s)
|
|||
interruptible->setChecked(s->interruptible);
|
||||
}
|
||||
|
||||
QHBoxLayout *mainLayout = new QHBoxLayout;
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{startScenes}}", startScenes},
|
||||
{"{{scenes}}", scenes},
|
||||
{"{{delay}}", delay},
|
||||
{"{{delayUnits}}", delayUnits},
|
||||
{"{{transitions}}", transitions},
|
||||
{"{{interruptible}}", interruptible}};
|
||||
placeWidgets(obs_module_text("AdvSceneSwitcher.sceneSequenceTab.entry"),
|
||||
mainLayout, widgetPlaceholders);
|
||||
setLayout(mainLayout);
|
||||
if (extendSequence) {
|
||||
QHBoxLayout *mainLayout = new QHBoxLayout;
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{scenes}}", scenes},
|
||||
{"{{delay}}", delay},
|
||||
{"{{delayUnits}}", delayUnits},
|
||||
{"{{transitions}}", transitions}};
|
||||
placeWidgets(
|
||||
obs_module_text(
|
||||
"AdvSceneSwitcher.sceneSequenceTab.extendEntry"),
|
||||
mainLayout, widgetPlaceholders);
|
||||
setLayout(mainLayout);
|
||||
} else {
|
||||
QHBoxLayout *startSequence = new QHBoxLayout;
|
||||
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{startScenes}}", startScenes},
|
||||
{"{{scenes}}", scenes},
|
||||
{"{{delay}}", delay},
|
||||
{"{{delayUnits}}", delayUnits},
|
||||
{"{{transitions}}", transitions},
|
||||
{"{{interruptible}}", interruptible}};
|
||||
placeWidgets(obs_module_text(
|
||||
"AdvSceneSwitcher.sceneSequenceTab.entry"),
|
||||
startSequence, widgetPlaceholders);
|
||||
|
||||
//exetend widgets placed here
|
||||
extendSequenceLayout = new QVBoxLayout;
|
||||
if (s) {
|
||||
if (!editExtendMode) {
|
||||
extendText->setText(makeExtendText(
|
||||
s->extendedSequence.get()));
|
||||
} else {
|
||||
auto cur = s->extendedSequence.get();
|
||||
while (cur != nullptr) {
|
||||
extendSequenceLayout->addWidget(
|
||||
new SequenceWidget(parent, cur,
|
||||
true, true));
|
||||
cur = cur->extendedSequence.get();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QHBoxLayout *extendSequenceControlsLayout = new QHBoxLayout;
|
||||
if (editExtendMode) {
|
||||
extendSequenceControlsLayout->addWidget(extend);
|
||||
extendSequenceControlsLayout->addWidget(reduce);
|
||||
}
|
||||
extendSequenceControlsLayout->addStretch();
|
||||
|
||||
QVBoxLayout *mainLayout = new QVBoxLayout;
|
||||
mainLayout->addLayout(startSequence);
|
||||
mainLayout->addLayout(extendSequenceLayout);
|
||||
mainLayout->addWidget(extendText);
|
||||
mainLayout->addLayout(extendSequenceControlsLayout);
|
||||
setLayout(mainLayout);
|
||||
}
|
||||
|
||||
switchData = s;
|
||||
|
||||
|
|
@ -493,6 +821,32 @@ void SequenceWidget::DelayUnitsChanged(int idx)
|
|||
UpdateDelay();
|
||||
}
|
||||
|
||||
void SequenceWidget::setExtendedSequenceStartScene()
|
||||
{
|
||||
switchData->extendedSequence->startScene = switchData->scene;
|
||||
switchData->extendedSequence->startTargetType = SwitchTargetType::Scene;
|
||||
|
||||
if (switchData->targetType == SwitchTargetType::SceneGroup) {
|
||||
switchData->extendedSequence->startScene = nullptr;
|
||||
switchData->extendedSequence->startTargetType =
|
||||
SwitchTargetType::SceneGroup;
|
||||
}
|
||||
}
|
||||
|
||||
void SequenceWidget::SceneChanged(const QString &text)
|
||||
{
|
||||
if (loading || !switchData) {
|
||||
return;
|
||||
}
|
||||
|
||||
SwitchWidget::SceneChanged(text);
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
|
||||
if (switchData->extendedSequence) {
|
||||
setExtendedSequenceStartScene();
|
||||
}
|
||||
}
|
||||
|
||||
void SequenceWidget::StartSceneChanged(const QString &text)
|
||||
{
|
||||
if (loading || !switchData) {
|
||||
|
|
@ -511,4 +865,45 @@ void SequenceWidget::InterruptibleChanged(int state)
|
|||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
switchData->interruptible = state;
|
||||
|
||||
auto cur = switchData->extendedSequence.get();
|
||||
while (cur != nullptr) {
|
||||
cur->interruptible = state;
|
||||
cur = cur->extendedSequence.get();
|
||||
}
|
||||
}
|
||||
|
||||
void SequenceWidget::UpdateExtendText()
|
||||
{
|
||||
extendText->setText(makeExtendText(switchData->extendedSequence.get()));
|
||||
}
|
||||
|
||||
void SequenceWidget::ExtendClicked()
|
||||
{
|
||||
if (loading || !switchData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
auto es = switchData->extend();
|
||||
|
||||
SequenceWidget *ew = new SequenceWidget(this->parentWidget(), es, true);
|
||||
extendSequenceLayout->addWidget(ew);
|
||||
}
|
||||
|
||||
void SequenceWidget::ReduceClicked()
|
||||
{
|
||||
if (loading || !switchData) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(switcher->m);
|
||||
switchData->reduce();
|
||||
|
||||
int count = extendSequenceLayout->count();
|
||||
auto item = extendSequenceLayout->takeAt(count - 1);
|
||||
if (item) {
|
||||
item->widget()->setVisible(false);
|
||||
delete item;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,6 +43,16 @@ void SwitcherData::Prune()
|
|||
sceneSequenceSwitches.erase(
|
||||
sceneSequenceSwitches.begin() + i--);
|
||||
}
|
||||
|
||||
auto cur = &s;
|
||||
while (cur != nullptr) {
|
||||
if (cur->extendedSequence &&
|
||||
!cur->extendedSequence->valid()) {
|
||||
cur->extendedSequence.reset(nullptr);
|
||||
s.activeSequence = nullptr;
|
||||
}
|
||||
cur = cur->extendedSequence.get();
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < sceneTransitions.size(); i++) {
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user