diff --git a/UI/data/locale/en-US.ini b/UI/data/locale/en-US.ini index 84442e874fd64b8e16c4f581f1be41ef53472277..2220cec3a0f7ccf6deacb3b976ce7ce68b276c44 100644 --- a/UI/data/locale/en-US.ini +++ b/UI/data/locale/en-US.ini @@ -76,6 +76,8 @@ Next="Next" Back="Back" Defaults="Defaults" HideMixer="Hide in Mixer" +TransitionOverride="Transition Override" +None="None" # warning if program already open AlreadyRunning.Title="OBS is already running" diff --git a/UI/window-basic-main-transitions.cpp b/UI/window-basic-main-transitions.cpp index 06ec0ace110ec6a252a9c4fd1c6e09f77ada63c1..590cdcc96560624420b0878ceded403198be4e7b 100644 --- a/UI/window-basic-main-transitions.cpp +++ b/UI/window-basic-main-transitions.cpp @@ -127,9 +127,17 @@ void OBSBasic::InitTransition(obs_source_t *transition) Qt::QueuedConnection); }; + auto onTransitionFullStop = [] (void *data, calldata_t*) { + OBSBasic *window = (OBSBasic*)data; + QMetaObject::invokeMethod(window, "TransitionFullyStopped", + Qt::QueuedConnection); + }; + signal_handler_t *handler = obs_source_get_signal_handler(transition); signal_handler_connect(handler, "transition_video_stop", onTransitionStop, this); + signal_handler_connect(handler, "transition_stop", + onTransitionFullStop, this); } static inline OBSSource GetTransitionComboItem(QComboBox *combo, int idx) @@ -242,6 +250,27 @@ void OBSBasic::TransitionStopped() swapScene = nullptr; } +static void OverrideTransition(OBSSource transition) +{ + obs_source_t *oldTransition = obs_get_output_source(0); + + if (transition != oldTransition) { + obs_transition_swap_begin(transition, oldTransition); + obs_set_output_source(0, transition); + obs_transition_swap_end(transition, oldTransition); + } + + obs_source_release(oldTransition); +} + +void OBSBasic::TransitionFullyStopped() +{ + if (overridingTransition) { + OverrideTransition(GetCurrentTransition()); + overridingTransition = false; + } +} + void OBSBasic::TransitionToScene(OBSSource source, bool force, bool direct) { obs_scene_t *scene = obs_scene_from_source(source); @@ -274,21 +303,43 @@ void OBSBasic::TransitionToScene(OBSSource source, bool force, bool direct) source = obs_scene_get_source(scene); } - obs_source_t *transition = obs_get_output_source(0); + OBSSource transition = obs_get_output_source(0); + obs_source_release(transition); if (force) { obs_transition_set(transition, source); if (api) api->on_event(OBS_FRONTEND_EVENT_SCENE_CHANGED); } else { + /* check for scene override */ + OBSData data = obs_source_get_private_settings(source); + obs_data_release(data); + + const char *trOverrideName = obs_data_get_string(data, + "transition"); + int duration = ui->transitionDuration->value(); + + if (trOverrideName && *trOverrideName) { + OBSSource trOverride = FindTransition(trOverrideName); + if (trOverride) { + transition = trOverride; + + obs_data_set_default_int(data, + "transition_duration", 300); + + duration = (int)obs_data_get_int(data, + "transition_duration"); + OverrideTransition(trOverride); + overridingTransition = true; + } + } + obs_transition_start(transition, OBS_TRANSITION_MODE_AUTO, - ui->transitionDuration->value(), source); + duration, source); } if (usingPreviewProgram && sceneDuplicationMode && !direct) obs_scene_release(scene); - - obs_source_release(transition); } static inline void SetComboTransition(QComboBox *combo, obs_source_t *tr) @@ -754,6 +805,88 @@ static inline void ResetQuickTransitionText(QuickTransition *qt) qt->button->setText(MakeQuickTransitionText(qt)); } +QMenu *OBSBasic::CreatePerSceneTransitionMenu() +{ + OBSSource scene = GetCurrentSceneSource(); + QMenu *menu = new QMenu(QTStr("TransitionOverride")); + QAction *action; + + OBSData data = obs_source_get_private_settings(scene); + obs_data_release(data); + + obs_data_set_default_int(data, "transition_duration", 300); + + const char *curTransition = obs_data_get_string(data, "transition"); + int curDuration = (int)obs_data_get_int(data, "transition_duration"); + + QSpinBox *duration = new QSpinBox(menu); + duration->setMinimum(50); + duration->setSuffix("ms"); + duration->setMaximum(20000); + duration->setSingleStep(50); + duration->setValue(curDuration); + + auto setTransition = [this] (QAction *action) + { + int idx = action->property("transition_index").toInt(); + OBSSource scene = GetCurrentSceneSource(); + OBSData data = obs_source_get_private_settings(scene); + obs_data_release(data); + + if (idx == -1) { + obs_data_set_string(data, "transition", ""); + return; + } + + OBSSource tr = GetTransitionComboItem(ui->transitions, idx); + const char *name = obs_source_get_name(tr); + + obs_data_set_string(data, "transition", name); + }; + + auto setDuration = [this] (int duration) + { + OBSSource scene = GetCurrentSceneSource(); + OBSData data = obs_source_get_private_settings(scene); + obs_data_release(data); + + obs_data_set_int(data, "transition_duration", duration); + }; + + connect(duration, (void (QSpinBox::*)(int))&QSpinBox::valueChanged, + setDuration); + + for (int i = -1; i < ui->transitions->count(); i++) { + const char *name = ""; + + if (i >= 0) { + OBSSource tr; + tr = GetTransitionComboItem(ui->transitions, i); + name = obs_source_get_name(tr); + } + + bool match = (name && strcmp(name, curTransition) == 0); + + if (!name || !*name) + name = Str("None"); + + action = menu->addAction(QT_UTF8(name)); + action->setProperty("transition_index", i); + action->setCheckable(true); + action->setChecked(match); + + connect(action, &QAction::triggered, + std::bind(setTransition, action)); + } + + QWidgetAction *durationAction = new QWidgetAction(menu); + durationAction->setDefaultWidget(duration); + + menu->addSeparator(); + menu->addAction(durationAction); + return menu; +} + QMenu *OBSBasic::CreateTransitionMenu(QWidget *parent, QuickTransition *qt) { QMenu *menu = new QMenu(parent); diff --git a/UI/window-basic-main.cpp b/UI/window-basic-main.cpp index 3f540f6db915c3280a0a4ba20658c8e64e99a6b3..6cc4fa9214770be4c9a3ee63c892b4bf2995bfde 100644 --- a/UI/window-basic-main.cpp +++ b/UI/window-basic-main.cpp @@ -3470,6 +3470,11 @@ void OBSBasic::on_scenes_customContextMenuRequested(const QPoint &pos) popup.addSeparator(); popup.addAction(QTStr("Filters"), this, SLOT(OpenSceneFilters())); + + popup.addSeparator(); + + QMenu *transitionMenu = CreatePerSceneTransitionMenu(); + popup.addMenu(transitionMenu); } popup.exec(QCursor::pos()); diff --git a/UI/window-basic-main.hpp b/UI/window-basic-main.hpp index 7474eedf115dfa3174ad4d5a6d721340f3bce9cb..c7bfc4d37e96d623cbb4d849bdf096a7bc240843 100644 --- a/UI/window-basic-main.hpp +++ b/UI/window-basic-main.hpp @@ -289,6 +289,8 @@ private: void RefreshQuickTransitions(); void CreateDefaultQuickTransitions(); + QMenu *CreatePerSceneTransitionMenu(); + QuickTransition *GetQuickTransition(int id); int GetQuickTransitionIdx(int id); QMenu *CreateTransitionMenu(QWidget *parent, QuickTransition *qt); @@ -317,6 +319,7 @@ private: obs_hotkey_id togglePreviewProgramHotkey = 0; obs_hotkey_id transitionHotkey = 0; int quickTransitionIdCounter = 1; + bool overridingTransition = false; int programX = 0, programY = 0; int programCX = 0, programCY = 0; @@ -427,6 +430,7 @@ private slots: void RenameTransition(); void TransitionClicked(); void TransitionStopped(); + void TransitionFullyStopped(); void TriggerQuickTransition(int id); void SetDeinterlacingMode();