From 16136b8741dd6f52ca58b76d838818bd3020193f Mon Sep 17 00:00:00 2001 From: WarmUpTill <19472752+WarmUpTill@users.noreply.github.com> Date: Fri, 10 Oct 2025 20:13:54 +0200 Subject: [PATCH] Rework scene switch action * Support "next scene" and "previous scene" * Support "scene at index" * Add canvas support * Rework layout --- data/locale/de-DE.ini | 2 - data/locale/en-US.ini | 15 +- data/locale/es-ES.ini | 1 - data/locale/fr-FR.ini | 2 - data/locale/ja-JP.ini | 3 - data/locale/pt-BR.ini | 3 - data/locale/ru-RU.ini | 1 - data/locale/tr-TR.ini | 1 - data/locale/zh-CN.ini | 5 +- plugins/base/macro-action-scene-switch.cpp | 429 ++++++++++++++++----- plugins/base/macro-action-scene-switch.hpp | 29 +- 11 files changed, 381 insertions(+), 110 deletions(-) diff --git a/data/locale/de-DE.ini b/data/locale/de-DE.ini index 38a3e890..de6b31c1 100644 --- a/data/locale/de-DE.ini +++ b/data/locale/de-DE.ini @@ -383,8 +383,6 @@ AdvSceneSwitcher.condition.variable.type.greaterThanVariable="ist größer als d ; Macro Actions AdvSceneSwitcher.action.scene="Szene wechseln" -AdvSceneSwitcher.action.scene.entry="Wechsle{{sceneTypes}}Szene zu{{scenes}}mittels{{transitions}}mit einer Dauer von{{duration}}Sekunden" -AdvSceneSwitcher.action.scene.entry.noDuration="Wechsle{{sceneTypes}}Szene zu{{scenes}}mittels{{transitions}}" AdvSceneSwitcher.action.scene.blockUntilTransitionDone="Warten, bis der Übergang zur Zielszene abgeschlossen ist" AdvSceneSwitcher.action.wait="Warten" AdvSceneSwitcher.action.wait.type.fixed="fixe" diff --git a/data/locale/en-US.ini b/data/locale/en-US.ini index 77fd92bf..7bcd71c9 100644 --- a/data/locale/en-US.ini +++ b/data/locale/en-US.ini @@ -822,12 +822,21 @@ AdvSceneSwitcher.condition.script="Script" # Macro Actions AdvSceneSwitcher.action.unknown="Unknown action" AdvSceneSwitcher.action.scene="Switch scene" +AdvSceneSwitcher.action.scene.action.fixed="Scene with name" +AdvSceneSwitcher.action.scene.action.sceneListNext="Next scene in scene list" +AdvSceneSwitcher.action.scene.action.sceneListPrevious="Previous scene in scene list" +AdvSceneSwitcher.action.scene.action.sceneListIndex="Scene in scene list at position" AdvSceneSwitcher.action.scene.type.program="Program" AdvSceneSwitcher.action.scene.type.preview="Preview" -AdvSceneSwitcher.action.scene.entry="Switch{{sceneTypes}}scene to{{scenes}}using{{transitions}}with a duration of{{duration}}seconds" -AdvSceneSwitcher.action.scene.entry.noDuration="Switch{{sceneTypes}}scene to{{scenes}}using{{transitions}}" -AdvSceneSwitcher.action.scene.entry.preview="Switch{{sceneTypes}}scene to{{scenes}}" +AdvSceneSwitcher.action.scene.layout.action.fixed="Switch{{sceneTypes}}to{{actions}}{{scenes}}" +AdvSceneSwitcher.action.scene.layout.action.list="Switch{{sceneTypes}}to{{actions}}on{{canvas}}" +AdvSceneSwitcher.action.scene.layout.action.list.noCanvasSelection="Switch{{sceneTypes}}to{{actions}}" +AdvSceneSwitcher.action.scene.layout.action.listIndex="Switch{{sceneTypes}}to{{actions}}{{index}}on{{canvas}}{{sceneNameAtIndex}}" +AdvSceneSwitcher.action.scene.layout.action.listIndex.noCanvasSelection="Switch{{sceneTypes}}to{{actions}}{{index}}{{sceneNameAtIndex}}" +AdvSceneSwitcher.action.scene.layout.transition="Use transition{{transitions}}" +AdvSceneSwitcher.action.scene.layout.transitionWithDuration="Use transition{{transitions}}with a duration of{{duration}}seconds" AdvSceneSwitcher.action.scene.blockUntilTransitionDone="Wait until transition to target scene is complete" +AdvSceneSwitcher.action.scene.canvasNotSupported="Not supported by the currently selected canvas \"%1\"" AdvSceneSwitcher.action.wait="Wait" AdvSceneSwitcher.action.wait.type.fixed="fixed" AdvSceneSwitcher.action.wait.type.random="random" diff --git a/data/locale/es-ES.ini b/data/locale/es-ES.ini index 1c041c81..f295a1f5 100644 --- a/data/locale/es-ES.ini +++ b/data/locale/es-ES.ini @@ -311,7 +311,6 @@ AdvSceneSwitcher.condition.stats.entry="{{stats}} esta {{condition}} {{value}}" ; Macro Actions AdvSceneSwitcher.action.scene="Cambiar escena" -AdvSceneSwitcher.action.scene.entry="Cambiar a la{{sceneTypes}}escena{{scenes}}usando{{transitions}}con una duración de{{duration}} segundos" AdvSceneSwitcher.action.scene.blockUntilTransitionDone="Espere hasta que se complete la transición a la escena de destino" AdvSceneSwitcher.action.wait="Esperar" AdvSceneSwitcher.action.wait.type.fixed="fijo" diff --git a/data/locale/fr-FR.ini b/data/locale/fr-FR.ini index e604782b..2dc19b24 100644 --- a/data/locale/fr-FR.ini +++ b/data/locale/fr-FR.ini @@ -459,8 +459,6 @@ AdvSceneSwitcher.condition.slideshow.updateInterval.tooltip="Les informations su AdvSceneSwitcher.action.scene="Changer de scène" AdvSceneSwitcher.action.scene.type.program="Programme" AdvSceneSwitcher.action.scene.type.preview="Aperçu" -AdvSceneSwitcher.action.scene.entry="Changer la scène{{sceneTypes}}{{scenes}}en utilisant{{transitions}}avec une durée de{{duration}}secondes" -AdvSceneSwitcher.action.scene.entry.noDuration="Changer la scène{{sceneTypes}}{{scenes}}en utilisant{{transitions}}" AdvSceneSwitcher.action.scene.blockUntilTransitionDone="Attendre que la transition vers la scène cible soit terminée" AdvSceneSwitcher.action.wait="Attendre" AdvSceneSwitcher.action.wait.type.fixed="fixe" diff --git a/data/locale/ja-JP.ini b/data/locale/ja-JP.ini index ea4881dd..d663e54a 100644 --- a/data/locale/ja-JP.ini +++ b/data/locale/ja-JP.ini @@ -810,9 +810,6 @@ AdvSceneSwitcher.action.unknown="不明なアクション" AdvSceneSwitcher.action.scene="シーン切り替え" AdvSceneSwitcher.action.scene.type.program="プログラム" AdvSceneSwitcher.action.scene.type.preview="プレビュー" -AdvSceneSwitcher.action.scene.entry="{{duration}}秒間の{{transitions}}を使用して、{{sceneTypes}}シーンを{{scenes}}に切り替えます" -AdvSceneSwitcher.action.scene.entry.noDuration="{{transitions}}を使用して{{sceneTypes}}シーンを{{scenes}}に切り替える" -AdvSceneSwitcher.action.scene.entry.preview="{{sceneTypes}}シーンを{{scenes}}に切り替えます" AdvSceneSwitcher.action.scene.blockUntilTransitionDone="目的のシーンへの遷移が完了するまで待ちます" ; AdvSceneSwitcher.action.wait="Wait" AdvSceneSwitcher.action.wait.type.fixed="指定" diff --git a/data/locale/pt-BR.ini b/data/locale/pt-BR.ini index a67c4b77..c60334ed 100644 --- a/data/locale/pt-BR.ini +++ b/data/locale/pt-BR.ini @@ -720,9 +720,6 @@ AdvSceneSwitcher.condition.noDevicesFoundWarning="Nenhum dispositivo USB detecta AdvSceneSwitcher.action.scene="Trocar cena" AdvSceneSwitcher.action.scene.type.program="Programa" AdvSceneSwitcher.action.scene.type.preview="Pré-visualização" -AdvSceneSwitcher.action.scene.entry="Trocar{{sceneTypes}}cena para{{scenes}}usando{{transitions}}com duração de{{duration}}segundos" -AdvSceneSwitcher.action.scene.entry.noDuration="Trocar{{sceneTypes}}cena para{{scenes}}usando{{transitions}}" -AdvSceneSwitcher.action.scene.entry.preview="Trocar{{sceneTypes}}cena para{{scenes}}" AdvSceneSwitcher.action.scene.blockUntilTransitionDone="Aguardar até que a transição para a cena de destino seja concluída" AdvSceneSwitcher.action.wait="Aguardar" AdvSceneSwitcher.action.wait.type.fixed="fixo" diff --git a/data/locale/ru-RU.ini b/data/locale/ru-RU.ini index 24cf1bcc..289da14a 100644 --- a/data/locale/ru-RU.ini +++ b/data/locale/ru-RU.ini @@ -114,7 +114,6 @@ AdvSceneSwitcher.condition.pluginState.entry="{{condition}}" ; Macro Actions AdvSceneSwitcher.action.scene="Переключить сцену" -AdvSceneSwitcher.action.scene.entry="Перейти к сцене{{sceneTypes}}{{scenes}}используя{{transitions}}с продолжительностью{{duration}}секунд" AdvSceneSwitcher.action.wait="Подождать" AdvSceneSwitcher.action.wait.type.fixed="фиксированный" AdvSceneSwitcher.action.wait.type.random="случайный" diff --git a/data/locale/tr-TR.ini b/data/locale/tr-TR.ini index 98bcf12b..1c984253 100644 --- a/data/locale/tr-TR.ini +++ b/data/locale/tr-TR.ini @@ -249,7 +249,6 @@ AdvSceneSwitcher.condition.openvr.entry.line3="HMD mevcut {{xPos}} x {{yPos}} x ; Macro Actions AdvSceneSwitcher.action.scene="Sahne Degistirici" -AdvSceneSwitcher.action.scene.entry="Sahneyi{{sceneTypes}}{{scenes}}kullanarak{{transitions}}süresi olan{{duration}}saniye" AdvSceneSwitcher.action.scene.blockUntilTransitionDone="Hedef sahneye geçiş tamamlanana kadar bekleyin" AdvSceneSwitcher.action.wait="Bekle" AdvSceneSwitcher.action.wait.type.fixed="sabit" diff --git a/data/locale/zh-CN.ini b/data/locale/zh-CN.ini index 7c9a5768..7a2fd7f7 100644 --- a/data/locale/zh-CN.ini +++ b/data/locale/zh-CN.ini @@ -698,7 +698,7 @@ AdvSceneSwitcher.condition.twitch.type.event.channel.user.moderator.deletion=" AdvSceneSwitcher.condition.twitch.type.event.channel.stream.online.live="开始直播" AdvSceneSwitcher.condition.twitch.type.event.channel.stream.online.playlist="开始播放列表" AdvSceneSwitcher.condition.twitch.type.event.channel.stream.online.watchParty="开始观看派对" -AdvSceneSwitcher.condition.twitch.type.event.channel.stream.online.premiere="开始首播 +AdvSceneSwitcher.condition.twitch.type.event.channel.stream.online.premiere="开始首播" AdvSceneSwitcher.condition.twitch.type.event.channel.stream.online.rerun="开始重播" AdvSceneSwitcher.condition.twitch.type.polling.channel.live="当前正在直播" AdvSceneSwitcher.condition.twitch.type.polling.channel.title="当前为头衔赛" @@ -779,9 +779,6 @@ AdvSceneSwitcher.action.unknown="未知操作" AdvSceneSwitcher.action.scene="切换场景" AdvSceneSwitcher.action.scene.type.program="程序" AdvSceneSwitcher.action.scene.type.preview="预览" -AdvSceneSwitcher.action.scene.entry="切换到场景{{sceneTypes}}{{scenes}}使用{{transitions}}时长{{duration}}秒" -AdvSceneSwitcher.action.scene.entry.noDuration="切换到场景{{sceneTypes}}{{scenes}}使用{{transitions}}" -AdvSceneSwitcher.action.scene.entry.preview="将{{sceneTypes}}场景切换到{{scenes}}" AdvSceneSwitcher.action.scene.blockUntilTransitionDone="等待转场到目标场景的动画完成" AdvSceneSwitcher.action.wait="等待" AdvSceneSwitcher.action.wait.type.fixed="固定" diff --git a/plugins/base/macro-action-scene-switch.cpp b/plugins/base/macro-action-scene-switch.cpp index d5d429ee..d2dad83d 100644 --- a/plugins/base/macro-action-scene-switch.cpp +++ b/plugins/base/macro-action-scene-switch.cpp @@ -19,14 +19,6 @@ bool MacroActionSwitchScene::_registered = MacroActionFactory::Register( {MacroActionSwitchScene::Create, MacroActionSwitchSceneEdit::Create, "AdvSceneSwitcher.action.scene"}); -const static std::map - sceneTypes = { - {MacroActionSwitchScene::SceneType::PROGRAM, - "AdvSceneSwitcher.action.scene.type.program"}, - {MacroActionSwitchScene::SceneType::PREVIEW, - "AdvSceneSwitcher.action.scene.type.preview"}, -}; - static void waitForTransitionChange(OBSWeakSource &transition, std::unique_lock *lock, Macro *macro) @@ -144,34 +136,92 @@ bool MacroActionSwitchScene::WaitForTransition(OBSWeakSource &scene, bool MacroActionSwitchScene::PerformAction() { - auto scene = _scene.GetScene(); + OBSWeakSource sceneToSwitchTo; + OBSWeakCanvas canvas; + + switch (_action) { + case Action::SCENE_NAME: + sceneToSwitchTo = _scene.GetScene(); + canvas = _scene.GetCanvas(); + break; + case Action::SCENE_LIST_NEXT: { + const auto activeScene = GetActiveCanvasScene(_canvas); + const int currentIndex = GetIndexOfScene(_canvas, activeScene); + sceneToSwitchTo = GetSceneAtIndex(_canvas, currentIndex + 1); + canvas = _canvas; + break; + } + case Action::SCENE_LIST_PREVIOUS: { + const auto activeScene = GetActiveCanvasScene(_canvas); + const int currentIndex = GetIndexOfScene(_canvas, activeScene); + sceneToSwitchTo = GetSceneAtIndex(_canvas, currentIndex - 1); + canvas = _canvas; + break; + } + case Action::SCENE_LIST_INDEX: { + sceneToSwitchTo = + GetSceneAtIndex(_canvas, _index.GetValue() - 1); + canvas = _canvas; + break; + } + default: + break; + } if (_sceneType == SceneType::PREVIEW) { - OBSSourceAutoRelease previewScneSource = - obs_weak_source_get_source(scene); - obs_frontend_set_current_preview_scene(previewScneSource); + OBSSourceAutoRelease previewSceneSource = + obs_weak_source_get_source(sceneToSwitchTo); + obs_frontend_set_current_preview_scene(previewSceneSource); return true; } auto transition = _transition.GetTransition(); - SwitchScene({scene, transition, (int)(_duration.Milliseconds())}, - obs_frontend_preview_program_mode_active()); - if (_blockUntilTransitionDone && scene && scene != GetCurrentScene()) { - return WaitForTransition(scene, transition); + SwitchScene({sceneToSwitchTo, transition, + (int)(_duration.Milliseconds())}, + obs_frontend_preview_program_mode_active(), canvas); + if (_blockUntilTransitionDone && sceneToSwitchTo && + sceneToSwitchTo != GetCurrentScene() && IsMainCanvas(canvas)) { + return WaitForTransition(sceneToSwitchTo, transition); } return true; } void MacroActionSwitchScene::LogAction() const { - ablog(LOG_INFO, "switch%s scene to '%s'", - _sceneType == SceneType::PREVIEW ? " preview" : "", - _scene.ToString(true).c_str()); + if (!ActionLoggingEnabled()) { + return; + } + + switch (_action) { + case Action::SCENE_NAME: + blog(LOG_INFO, "switch %s scene to '%s'", + _sceneType == SceneType::PREVIEW ? " preview" : "program", + _scene.ToString(true).c_str()); + break; + case Action::SCENE_LIST_NEXT: + blog(LOG_INFO, "switch %s to next scene in scene list", + _sceneType == SceneType::PREVIEW ? " preview" : "program"); + break; + case Action::SCENE_LIST_PREVIOUS: + blog(LOG_INFO, "switch %s to previous scene in scene list", + _sceneType == SceneType::PREVIEW ? " preview" : "program"); + break; + case Action::SCENE_LIST_INDEX: + blog(LOG_INFO, "switch %s to scene at index %d in scene list", + _sceneType == SceneType::PREVIEW ? " preview" : "program", + _index.GetValue()); + break; + default: + break; + } } bool MacroActionSwitchScene::Save(obs_data_t *obj) const { MacroAction::Save(obj); + obs_data_set_int(obj, "action", static_cast(_action)); + obs_data_set_string(obj, "canvas", GetWeakCanvasName(_canvas).c_str()); + _index.Save(obj, "index"); _scene.Save(obj); _transition.Save(obj); _duration.Save(obj); @@ -184,6 +234,16 @@ bool MacroActionSwitchScene::Save(obs_data_t *obj) const bool MacroActionSwitchScene::Load(obs_data_t *obj) { MacroAction::Load(obj); + _action = static_cast(obs_data_get_int(obj, "action")); + if (obs_data_has_user_value(obj, "canvas")) { + _canvas = + GetWeakCanvasByName(obs_data_get_string(obj, "canvas")); + } else { + OBSCanvasAutoRelease main = obs_get_main_canvas(); + _canvas = OBSGetWeakRef(main); + } + + _index.Save(obj, "index"); _scene.Load(obj); _transition.Load(obj); _duration.Load(obj); @@ -195,7 +255,7 @@ bool MacroActionSwitchScene::Load(obs_data_t *obj) std::string MacroActionSwitchScene::GetShortDesc() const { - return _scene.ToString(); + return _action == Action::SCENE_NAME ? _scene.ToString() : ""; } std::shared_ptr MacroActionSwitchScene::Create(Macro *m) @@ -214,61 +274,128 @@ void MacroActionSwitchScene::ResolveVariablesToFixedValues() _duration.ResolveVariables(); } +template +static void populate(QComboBox *list, const std::map &data) +{ + for (const auto &[value, name] : data) { + list->addItem(obs_module_text(name.c_str()), + static_cast(value)); + } +} + static inline void populateTypeSelection(QComboBox *list) { - for (const auto &[_, name] : sceneTypes) { - list->addItem(obs_module_text(name.c_str())); - } + static const std::map + types = { + {MacroActionSwitchScene::SceneType::PROGRAM, + "AdvSceneSwitcher.action.scene.type.program"}, + {MacroActionSwitchScene::SceneType::PREVIEW, + "AdvSceneSwitcher.action.scene.type.preview"}, + }; + + populate(list, types); +} + +static inline void populateActionSelection(QComboBox *list) +{ + static const std::map + actions = { + {MacroActionSwitchScene::Action::SCENE_NAME, + "AdvSceneSwitcher.action.scene.action.fixed"}, + {MacroActionSwitchScene::Action::SCENE_LIST_NEXT, + "AdvSceneSwitcher.action.scene.action.sceneListNext"}, + {MacroActionSwitchScene::Action::SCENE_LIST_PREVIOUS, + "AdvSceneSwitcher.action.scene.action.sceneListPrevious"}, + {MacroActionSwitchScene::Action::SCENE_LIST_INDEX, + "AdvSceneSwitcher.action.scene.action.sceneListIndex"}, + }; + + populate(list, actions); } MacroActionSwitchSceneEdit::MacroActionSwitchSceneEdit( QWidget *parent, std::shared_ptr entryData) : QWidget(parent), + _actions(new QComboBox(this)), + _index(new VariableSpinBox(this)), _scenes(new SceneSelectionWidget(this, true, true, true)), _transitions(new TransitionSelectionWidget(this)), - _duration(new DurationSelection(parent, false)), + _duration(new DurationSelection(this, false)), _blockUntilTransitionDone(new QCheckBox(obs_module_text( "AdvSceneSwitcher.action.scene.blockUntilTransitionDone"))), - _sceneTypes(new QComboBox()), - _entryLayout(new QHBoxLayout()) + _sceneTypes(new QComboBox(this)), + _canvas(new CanvasSelection(this)), + _sceneNameAtIndex(new QLabel(this)), + _updateSceneNameTimer(new QTimer(this)), + _actionLayout(new QHBoxLayout()), + _transitionLayout(new QHBoxLayout()), + _notSupportedWarning(new QLabel(this)) { + _index->setMinimum(1); + _index->setMaximum(999); + _duration->SpinBox()->setSpecialValueText("-"); + populateTypeSelection(_sceneTypes); + populateActionSelection(_actions); - QWidget::connect(_scenes, SIGNAL(SceneChanged(const SceneSelection &)), - this, SLOT(SceneChanged(const SceneSelection &))); - QWidget::connect(_transitions, - SIGNAL(TransitionChanged(const TransitionSelection &)), - this, - SLOT(TransitionChanged(const TransitionSelection &))); - QWidget::connect(_duration, SIGNAL(DurationChanged(const Duration &)), - this, SLOT(DurationChanged(const Duration &))); - QWidget::connect(_blockUntilTransitionDone, SIGNAL(stateChanged(int)), - this, SLOT(BlockUntilTransitionDoneChanged(int))); - QWidget::connect(_sceneTypes, SIGNAL(currentIndexChanged(int)), this, - SLOT(SceneTypeChanged(int))); - - PlaceWidgets(obs_module_text("AdvSceneSwitcher.action.scene.entry"), - _entryLayout, - {{"{{scenes}}", _scenes}, - {"{{transitions}}", _transitions}, - {"{{duration}}", _duration}, - {"{{sceneTypes}}", _sceneTypes}}); + SetupWidgetConnections(); auto mainLayout = new QVBoxLayout; - mainLayout->addLayout(_entryLayout); + mainLayout->addLayout(_actionLayout); + mainLayout->addLayout(_transitionLayout); mainLayout->addWidget(_blockUntilTransitionDone); + mainLayout->addWidget(_notSupportedWarning); setLayout(mainLayout); _entryData = entryData; - _sceneTypes->setCurrentIndex(static_cast(_entryData->_sceneType)); + SetWidgetData(); + + SetWidgetVisibility(); + _loading = false; +} + +void MacroActionSwitchSceneEdit::SetupWidgetConnections() const +{ + connect(_actions, SIGNAL(currentIndexChanged(int)), this, + SLOT(ActionChanged(int))); + connect(_sceneTypes, SIGNAL(currentIndexChanged(int)), this, + SLOT(SceneTypeChanged(int))); + connect(_canvas, SIGNAL(CanvasChanged(const OBSWeakCanvas &)), this, + SLOT(CanvasChanged(const OBSWeakCanvas &))); + connect(_index, + SIGNAL(NumberVariableChanged(const NumberVariable &)), + this, SLOT(IndexChanged(const NumberVariable &))); + connect(_scenes, SIGNAL(SceneChanged(const SceneSelection &)), this, + SLOT(SceneChanged(const SceneSelection &))); + connect(_scenes, SIGNAL(CanvasChanged(const OBSWeakCanvas &)), this, + SLOT(SceneSelectionCanvasChanged(const OBSWeakCanvas &))); + connect(_transitions, + SIGNAL(TransitionChanged(const TransitionSelection &)), this, + SLOT(TransitionChanged(const TransitionSelection &))); + connect(_duration, SIGNAL(DurationChanged(const Duration &)), this, + SLOT(DurationChanged(const Duration &))); + connect(_blockUntilTransitionDone, SIGNAL(stateChanged(int)), this, + SLOT(BlockUntilTransitionDoneChanged(int))); + connect(_updateSceneNameTimer, SIGNAL(timeout()), this, + SLOT(UpdateSceneNameAtIndex())); + + _updateSceneNameTimer->setInterval(300); + _updateSceneNameTimer->start(); +} + +void MacroActionSwitchSceneEdit::SetWidgetData() const +{ + _actions->setCurrentIndex( + _actions->findData(static_cast(_entryData->_action))); + _index->SetValue(_entryData->_index); + _sceneTypes->setCurrentIndex(_sceneTypes->findData( + static_cast(_entryData->_sceneType))); _scenes->SetScene(_entryData->_scene); _transitions->SetTransition(_entryData->_transition); _duration->SetDuration(_entryData->_duration); _blockUntilTransitionDone->setChecked( _entryData->_blockUntilTransitionDone); - SetWidgetVisibility(); - _loading = false; } void MacroActionSwitchSceneEdit::DurationChanged(const Duration &dur) @@ -283,58 +410,182 @@ void MacroActionSwitchSceneEdit::BlockUntilTransitionDoneChanged(int state) _entryData->_blockUntilTransitionDone = state; } -void MacroActionSwitchSceneEdit::SceneTypeChanged(int value) +void MacroActionSwitchSceneEdit::SceneTypeChanged(int) { GUARD_LOADING_AND_LOCK(); - _entryData->_sceneType = - static_cast(value); + _entryData->_sceneType = static_cast( + _sceneTypes->currentData().toInt()); SetWidgetVisibility(); } +void MacroActionSwitchSceneEdit::CanvasChanged(const OBSWeakCanvas &canvas) +{ + GUARD_LOADING_AND_LOCK(); + _entryData->_canvas = canvas; + SetWidgetVisibility(); +} + +void MacroActionSwitchSceneEdit::SceneSelectionCanvasChanged( + const OBSWeakCanvas &) +{ + SetWidgetVisibility(); +} + +void MacroActionSwitchSceneEdit::ActionChanged(int value) +{ + GUARD_LOADING_AND_LOCK(); + _entryData->_action = static_cast( + _actions->currentData().toInt()); + SetWidgetVisibility(); +} + +void MacroActionSwitchSceneEdit::IndexChanged(const NumberVariable &value) +{ + GUARD_LOADING_AND_LOCK(); + _entryData->_index = value; +} + +void MacroActionSwitchSceneEdit::UpdateSceneNameAtIndex() +{ + if (!_entryData) { + return; + } + + auto weakSource = + GetSceneAtIndex(_entryData->_canvas, _entryData->_index - 1); + if (!weakSource) { + _sceneNameAtIndex->setText(""); + return; + } + + OBSSourceAutoRelease source = OBSGetStrongRef(weakSource); + auto name = obs_source_get_name(source); + if (!name) { + _sceneNameAtIndex->setText(""); + return; + } + + const QString text = QString("(") + name + ")"; + _sceneNameAtIndex->setText(text); +} + +static bool +shouldShowDurationInTransitionLayout(MacroActionSwitchScene::SceneType type, + const TransitionSelection &transition) +{ + if (type == MacroActionSwitchScene::SceneType::PREVIEW) { + return false; + } + + if (transition.GetType() != TransitionSelection::Type::TRANSITION) { + return true; + } + + if (isUsingFixedLengthTransition(transition.GetTransition())) { + return false; + } + + return true; +} + void MacroActionSwitchSceneEdit::SetWidgetVisibility() { - _entryLayout->removeWidget(_scenes); - _entryLayout->removeWidget(_transitions); - _entryLayout->removeWidget(_duration); - _entryLayout->removeWidget(_sceneTypes); - ClearLayout(_entryLayout); - std::unordered_map widgetPlaceholders = { - {"{{scenes}}", _scenes}, - {"{{transitions}}", _transitions}, - {"{{duration}}", _duration}, + _actionLayout->removeWidget(_sceneTypes); + _actionLayout->removeWidget(_actions); + _actionLayout->removeWidget(_scenes); + _actionLayout->removeWidget(_canvas); + _actionLayout->removeWidget(_index); + _actionLayout->removeWidget(_sceneNameAtIndex); + ClearLayout(_actionLayout); + + const int canvasCount = GetCanvasCount(); + const auto &action = _entryData->_action; + + _scenes->setVisible(action == + MacroActionSwitchScene::Action::SCENE_NAME); + _canvas->setVisible( + action != MacroActionSwitchScene::Action::SCENE_NAME && + canvasCount > 1); + _index->setVisible(action == + MacroActionSwitchScene::Action::SCENE_LIST_INDEX); + _sceneNameAtIndex->setVisible( + action == MacroActionSwitchScene::Action::SCENE_LIST_INDEX); + + const std::unordered_map actionWidgets = { + {"{{actions}}", _actions}, {"{{sceneTypes}}", _sceneTypes}, - }; + {"{{scenes}}", _scenes}, + {"{{canvas}}", _canvas}, + {"{{index}}", _index}, + {"{{sceneNameAtIndex}}", _sceneNameAtIndex}}; - if (_entryData->_sceneType == - MacroActionSwitchScene::SceneType::PREVIEW) { - _transitions->hide(); - _duration->hide(); - PlaceWidgets( - obs_module_text( - "AdvSceneSwitcher.action.scene.entry.preview"), - _entryLayout, widgetPlaceholders); - return; + const char *layoutString = ""; + switch (action) { + case MacroActionSwitchScene::Action::SCENE_NAME: + layoutString = + "AdvSceneSwitcher.action.scene.layout.action.fixed"; + break; + case MacroActionSwitchScene::Action::SCENE_LIST_NEXT: + case MacroActionSwitchScene::Action::SCENE_LIST_PREVIOUS: + layoutString = + (canvasCount > 1) + ? "AdvSceneSwitcher.action.scene.layout.action.list" + : "AdvSceneSwitcher.action.scene.layout.action.list.noCanvasSelection"; + break; + case MacroActionSwitchScene::Action::SCENE_LIST_INDEX: + layoutString = + (canvasCount > 1) + ? "AdvSceneSwitcher.action.scene.layout.action.listIndex" + : "AdvSceneSwitcher.action.scene.layout.action.listIndex.noCanvasSelection"; + break; + default: + break; } - _transitions->show(); - if (_entryData->_transition.GetType() != - TransitionSelection::Type::TRANSITION) { - _duration->show(); - } - const bool fixedDuration = isUsingFixedLengthTransition( - _entryData->_transition.GetTransition()); - _duration->setVisible(!fixedDuration); + PlaceWidgets(obs_module_text(layoutString), _actionLayout, + actionWidgets); - if (fixedDuration) { - PlaceWidgets( - obs_module_text( - "AdvSceneSwitcher.action.scene.entry.noDuration"), - _entryLayout, widgetPlaceholders); - } else { - PlaceWidgets( - obs_module_text("AdvSceneSwitcher.action.scene.entry"), - _entryLayout, widgetPlaceholders); - } + _transitionLayout->removeWidget(_transitions); + _transitionLayout->removeWidget(_duration); + ClearLayout(_transitionLayout); + + const bool shouldShowDuration = shouldShowDurationInTransitionLayout( + _entryData->_sceneType, _entryData->_transition); + _duration->setVisible(shouldShowDuration); + + layoutString = + shouldShowDuration + ? "AdvSceneSwitcher.action.scene.layout.transitionWithDuration" + : "AdvSceneSwitcher.action.scene.layout.transition"; + PlaceWidgets(obs_module_text(layoutString), _transitionLayout, + {{"{{transitions}}", _transitions}, + {"{{duration}}", _duration}}); + + const auto canvas = action == MacroActionSwitchScene::Action::SCENE_NAME + ? _entryData->_scene.GetCanvas() + : _entryData->_canvas; + const bool isMainCanvas = IsMainCanvas(canvas); + SetLayoutVisible(_transitionLayout, isMainCanvas); + _blockUntilTransitionDone->setVisible(isMainCanvas); + + _scenes->setVisible(action == + MacroActionSwitchScene::Action::SCENE_NAME); + _index->setVisible(action == + MacroActionSwitchScene::Action::SCENE_LIST_INDEX); + _sceneNameAtIndex->setVisible( + action == MacroActionSwitchScene::Action::SCENE_LIST_INDEX); + + const QString fmt = obs_module_text( + "AdvSceneSwitcher.action.scene.canvasNotSupported"); + _notSupportedWarning->setText( + fmt.arg(QString::fromStdString(GetWeakCanvasName(canvas)))); + _notSupportedWarning->setVisible( + !IsMainCanvas(canvas) && + _entryData->_sceneType == + MacroActionSwitchScene::SceneType::PREVIEW); + + adjustSize(); + updateGeometry(); } void MacroActionSwitchSceneEdit::SceneChanged(const SceneSelection &s) diff --git a/plugins/base/macro-action-scene-switch.hpp b/plugins/base/macro-action-scene-switch.hpp index 36eab5cf..eee7461e 100644 --- a/plugins/base/macro-action-scene-switch.hpp +++ b/plugins/base/macro-action-scene-switch.hpp @@ -6,6 +6,8 @@ #include +#include + namespace advss { class MacroActionSwitchScene : public MacroAction { @@ -21,8 +23,19 @@ public: std::shared_ptr Copy() const; void ResolveVariablesToFixedValues(); + enum class Action { + SCENE_NAME, + SCENE_LIST_NEXT, + SCENE_LIST_PREVIOUS, + SCENE_LIST_INDEX, + }; + Action _action = Action::SCENE_NAME; + enum class SceneType { PROGRAM, PREVIEW }; SceneType _sceneType = SceneType::PROGRAM; + + OBSWeakCanvas _canvas; + IntVariable _index = 1; SceneSelection _scene; TransitionSelection _transition; Duration _duration; @@ -57,18 +70,32 @@ private slots: void DurationChanged(const Duration &seconds); void BlockUntilTransitionDoneChanged(int state); void SceneTypeChanged(int); + void CanvasChanged(const OBSWeakCanvas &); + void SceneSelectionCanvasChanged(const OBSWeakCanvas &); + void ActionChanged(int); + void IndexChanged(const NumberVariable &); + void UpdateSceneNameAtIndex(); signals: void HeaderInfoChanged(const QString &); private: + void SetupWidgetConnections() const; + void SetWidgetData() const; void SetWidgetVisibility(); + QComboBox *_actions; + VariableSpinBox *_index; SceneSelectionWidget *_scenes; TransitionSelectionWidget *_transitions; DurationSelection *_duration; QCheckBox *_blockUntilTransitionDone; QComboBox *_sceneTypes; - QHBoxLayout *_entryLayout; + CanvasSelection *_canvas; + QLabel *_sceneNameAtIndex; + QTimer *_updateSceneNameTimer; + QHBoxLayout *_actionLayout; + QHBoxLayout *_transitionLayout; + QLabel *_notSupportedWarning; std::shared_ptr _entryData; bool _loading = true;