diff --git a/frontend/OBSStudioAPI.cpp b/frontend/OBSStudioAPI.cpp index 7c710d0144de07..e9cbf7a8353537 100644 --- a/frontend/OBSStudioAPI.cpp +++ b/frontend/OBSStudioAPI.cpp @@ -74,14 +74,11 @@ void OBSStudioAPI::obs_frontend_set_current_scene(obs_source_t *scene) void OBSStudioAPI::obs_frontend_get_transitions(struct obs_frontend_source_list *sources) { - for (int i = 0; i < main->ui->transitions->count(); i++) { - OBSSource tr = main->ui->transitions->itemData(i).value(); + for (const auto &[uuid, transition] : main->transitions) { + obs_source_t *source = transition; - if (!tr) - continue; - - if (obs_source_get_ref(tr) != nullptr) - da_push_back(sources->sources, &tr); + if (obs_source_get_ref(source) != nullptr) + da_push_back(sources->sources, &source); } } @@ -98,12 +95,12 @@ void OBSStudioAPI::obs_frontend_set_current_transition(obs_source_t *transition) int OBSStudioAPI::obs_frontend_get_transition_duration() { - return main->ui->transitionDuration->value(); + return main->GetTransitionDuration(); } void OBSStudioAPI::obs_frontend_set_transition_duration(int duration) { - QMetaObject::invokeMethod(main->ui->transitionDuration, "setValue", Q_ARG(int, duration)); + QMetaObject::invokeMethod(main, "SetTransitionDuration", Q_ARG(int, duration)); } void OBSStudioAPI::obs_frontend_release_tbar() diff --git a/frontend/widgets/OBSBasic.cpp b/frontend/widgets/OBSBasic.cpp index ffe42be300fd2e..b5af325b0e36ae 100644 --- a/frontend/widgets/OBSBasic.cpp +++ b/frontend/widgets/OBSBasic.cpp @@ -287,6 +287,40 @@ OBSBasic::OBSBasic(QWidget *parent) : OBSMainWindow(parent), undo_s(ui), ui(new connect(controls, &OBSBasicControls::SettingsButtonClicked, this, &OBSBasic::on_action_Settings_triggered); + /* Set up transitions combobox connections */ + connect(this, &OBSBasic::TransitionAdded, this, [this](const QString &name, const QString &uuid) { + QSignalBlocker sb(ui->transitions); + ui->transitions->addItem(name, uuid); + }); + connect(this, &OBSBasic::TransitionRenamed, this, [this](const QString &uuid, const QString &newName) { + QSignalBlocker sb(ui->transitions); + ui->transitions->setItemText(ui->transitions->findData(uuid), newName); + }); + connect(this, &OBSBasic::TransitionRemoved, this, [this](const QString &uuid) { + QSignalBlocker sb(ui->transitions); + ui->transitions->removeItem(ui->transitions->findData(uuid)); + }); + connect(this, &OBSBasic::TransitionsCleared, this, [this]() { + QSignalBlocker sb(ui->transitions); + ui->transitions->clear(); + }); + + connect(this, &OBSBasic::CurrentTransitionChanged, this, [this](const QString &uuid) { + QSignalBlocker sb(ui->transitions); + ui->transitions->setCurrentIndex(ui->transitions->findData(uuid)); + }); + + connect(ui->transitions, &QComboBox::currentIndexChanged, this, + [this]() { SetCurrentTransition(ui->transitions->currentData().toString()); }); + + connect(this, &OBSBasic::TransitionDurationChanged, this, [this](int duration) { + QSignalBlocker sb(ui->transitionDuration); + ui->transitionDuration->setValue(duration); + }); + + connect(ui->transitionDuration, &QSpinBox::valueChanged, this, + [this](int value) { SetTransitionDuration(value); }); + startingDockLayout = saveState(); statsDock = new OBSDock(); @@ -967,7 +1001,7 @@ void OBSBasic::OBSInit() } /* Modules can access frontend information (i.e. profile and scene collection data) during their initialization, and some modules (e.g. obs-websockets) are known to use the filesystem location of the current profile in their own code. - + Thus the profile and scene collection discovery needs to happen before any access to that information (but after intializing global settings) to ensure legacy code gets valid path information. */ RefreshSceneCollections(true); diff --git a/frontend/widgets/OBSBasic.hpp b/frontend/widgets/OBSBasic.hpp index 65dbe5ecee66a0..75d74f045ea3ab 100644 --- a/frontend/widgets/OBSBasic.hpp +++ b/frontend/widgets/OBSBasic.hpp @@ -1466,6 +1466,14 @@ private slots: std::vector safeModeTransitions; QPointer transitionButton; QPointer perSceneTransitionMenu; + + std::unordered_map transitions; + // FIXME: Any code accessing this collection relies on order of insertion + std::vector transitionUuids; + // FIXME: Replace usages of a name to identify a transition + std::unordered_map transitionNameToUuids; + int transitionDuration; + std::string currentTransitionUuid; obs_source_t *fadeTransition; obs_source_t *cutTransition; std::vector quickTransitions; @@ -1519,6 +1527,8 @@ private slots: void PasteShowHideTransition(obs_sceneitem_t *item, bool show, obs_source_t *tr, int duration); + void UpdateCurrentTransition(const std::string &uuid, bool setTransition); + public slots: void SetCurrentScene(OBSSource scene, bool force = false); @@ -1528,6 +1538,10 @@ public slots: void TransitionToScene(OBSSource scene, bool force = false, bool quickTransition = false, int quickDuration = 0, bool black = false, bool manual = false); + void SetCurrentTransition(const QString &uuid); + + void SetTransitionDuration(int duration); + private slots: void AddTransition(const char *id); void RenameTransition(OBSSource transition); @@ -1540,15 +1554,23 @@ private slots: void TBarChanged(int value); void TBarReleased(); - void on_transitions_currentIndexChanged(int index); void on_transitionAdd_clicked(); void on_transitionRemove_clicked(); void on_transitionProps_clicked(); - void on_transitionDuration_valueChanged(); void ShowTransitionProperties(); void HideTransitionProperties(); +signals: + void TransitionAdded(const QString &name, const QString &uuid); + void TransitionRenamed(const QString &uuid, const QString &newName); + void TransitionRemoved(const QString &uuid); + void TransitionsCleared(); + + void CurrentTransitionChanged(const QString &uuid); + + void TransitionDurationChanged(const int &duration); + public: int GetTransitionDuration(); int GetTbarPosition(); diff --git a/frontend/widgets/OBSBasic_SceneCollections.cpp b/frontend/widgets/OBSBasic_SceneCollections.cpp index 88a4a6e6e5a678..ee9e4e6086f735 100644 --- a/frontend/widgets/OBSBasic_SceneCollections.cpp +++ b/frontend/widgets/OBSBasic_SceneCollections.cpp @@ -882,13 +882,12 @@ void OBSBasic::Save(SceneCollection &collection) curProgramScene = obs_scene_get_source(scene); OBSDataArrayAutoRelease sceneOrder = SaveSceneListOrder(); - OBSDataArrayAutoRelease transitions = SaveTransitions(); + OBSDataArrayAutoRelease transitionsData = SaveTransitions(); OBSDataArrayAutoRelease quickTrData = SaveQuickTransitions(); OBSDataArrayAutoRelease savedProjectorList = SaveProjectors(); OBSDataArrayAutoRelease savedCanvases = OBS::Canvas::SaveCanvases(canvases); - OBSDataAutoRelease saveData = GenerateSaveData(sceneOrder, quickTrData, ui->transitionDuration->value(), - transitions, scene, curProgramScene, savedProjectorList, - savedCanvases); + OBSDataAutoRelease saveData = GenerateSaveData(sceneOrder, quickTrData, transitionDuration, transitionsData, + scene, curProgramScene, savedProjectorList, savedCanvases); obs_data_set_bool(saveData, "preview_locked", ui->preview->Locked()); obs_data_set_bool(saveData, "scaling_enabled", ui->preview->IsFixedScaling()); @@ -1004,7 +1003,7 @@ void OBSBasic::CreateDefaultScene(bool firstStart) ClearSceneData(); InitDefaultTransitions(); CreateDefaultQuickTransitions(); - ui->transitionDuration->setValue(300); + transitionDuration = 300; SetTransition(fadeTransition); updateRemigrationMenuItem(SceneCoordinateMode::Relative, ui->actionRemigrateSceneCollection); @@ -1172,7 +1171,7 @@ void OBSBasic::LoadData(obs_data_t *data, SceneCollection &collection) OBSDataArrayAutoRelease sceneOrder = obs_data_get_array(data, "scene_order"); OBSDataArrayAutoRelease sources = obs_data_get_array(data, "sources"); OBSDataArrayAutoRelease groups = obs_data_get_array(data, "groups"); - OBSDataArrayAutoRelease transitions = obs_data_get_array(data, "transitions"); + OBSDataArrayAutoRelease transitionsData = obs_data_get_array(data, "transitions"); OBSDataArrayAutoRelease collection_canvases = obs_data_get_array(data, "canvases"); const char *sceneName = obs_data_get_string(data, "current_scene"); const char *programSceneName = obs_data_get_string(data, "current_program_scene"); @@ -1295,8 +1294,8 @@ void OBSBasic::LoadData(obs_data_t *data, SceneCollection &collection) if (resetVideo) ResetVideo(); - if (transitions) - LoadTransitions(transitions, AddMissingFiles, files); + if (transitionsData) + LoadTransitions(transitionsData, AddMissingFiles, files); if (sceneOrder) LoadSceneListOrder(sceneOrder); @@ -1304,7 +1303,7 @@ void OBSBasic::LoadData(obs_data_t *data, SceneCollection &collection) if (!curTransition) curTransition = fadeTransition; - ui->transitionDuration->setValue(newDuration); + transitionDuration = newDuration; SetTransition(curTransition); retryScene: @@ -1499,7 +1498,12 @@ void OBSBasic::ClearSceneData() ClearListItems(ui->scenes); ui->sources->Clear(); ClearQuickTransitions(); - ui->transitions->clear(); + + currentTransitionUuid.clear(); + transitions.clear(); + transitionNameToUuids.clear(); + transitionUuids.clear(); + emit TransitionsCleared(); ClearProjectors(); diff --git a/frontend/widgets/OBSBasic_StudioMode.cpp b/frontend/widgets/OBSBasic_StudioMode.cpp index 042c269c12b5f1..d492f8f298785c 100644 --- a/frontend/widgets/OBSBasic_StudioMode.cpp +++ b/frontend/widgets/OBSBasic_StudioMode.cpp @@ -76,8 +76,8 @@ void OBSBasic::CreateProgramOptions() transitionButton = new QPushButton(QTStr("Transition")); transitionButton->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); - QHBoxLayout *quickTransitions = new QHBoxLayout(); - quickTransitions->setSpacing(2); + QHBoxLayout *quickTransitionsLayout = new QHBoxLayout(); + quickTransitionsLayout->setSpacing(2); QPushButton *addQuickTransition = new QPushButton(); addQuickTransition->setProperty("class", "icon-plus"); @@ -85,8 +85,8 @@ void OBSBasic::CreateProgramOptions() QLabel *quickTransitionsLabel = new QLabel(QTStr("QuickTransitions")); quickTransitionsLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); - quickTransitions->addWidget(quickTransitionsLabel); - quickTransitions->addWidget(addQuickTransition); + quickTransitionsLayout->addWidget(quickTransitionsLabel); + quickTransitionsLayout->addWidget(addQuickTransition); mainButtonLayout->addWidget(transitionButton); mainButtonLayout->addWidget(configTransitions); @@ -102,7 +102,7 @@ void OBSBasic::CreateProgramOptions() layout->addStretch(0); layout->addLayout(mainButtonLayout); - layout->addLayout(quickTransitions); + layout->addLayout(quickTransitionsLayout); layout->addWidget(tBar); layout->addStretch(0); diff --git a/frontend/widgets/OBSBasic_Transitions.cpp b/frontend/widgets/OBSBasic_Transitions.cpp index aeb898d0633b69..b76f433762fc73 100644 --- a/frontend/widgets/OBSBasic_Transitions.cpp +++ b/frontend/widgets/OBSBasic_Transitions.cpp @@ -17,6 +17,8 @@ #include "OBSBasic.hpp" +#include + #include #include #include @@ -47,7 +49,7 @@ static inline QString MakeQuickTransitionText(QuickTransition *qt) void OBSBasic::InitDefaultTransitions() { - std::vector transitions; + std::vector defaultTransitions; size_t idx = 0; const char *id; @@ -59,7 +61,7 @@ void OBSBasic::InitDefaultTransitions() OBSSourceAutoRelease tr = obs_source_create_private(id, name, NULL); InitTransition(tr); - transitions.emplace_back(tr); + defaultTransitions.emplace_back(tr); if (strcmp(id, "fade_transition") == 0) fadeTransition = tr; @@ -68,9 +70,17 @@ void OBSBasic::InitDefaultTransitions() } } - for (OBSSource &tr : transitions) { - ui->transitions->addItem(QT_UTF8(obs_source_get_name(tr)), QVariant::fromValue(OBSSource(tr))); + for (OBSSource &tr : defaultTransitions) { + std::string uuid = obs_source_get_uuid(tr); + + transitions.insert({uuid, OBSSource(tr)}); + transitionNameToUuids.insert({obs_source_get_name(tr), uuid}); + transitionUuids.push_back(uuid); + + emit TransitionAdded(QT_UTF8(obs_source_get_name(tr)), QString::fromStdString(uuid)); } + + UpdateCurrentTransition(transitionUuids.back(), true); } void OBSBasic::AddQuickTransitionHotkey(QuickTransition *qt) @@ -198,14 +208,15 @@ obs_data_array_t *OBSBasic::SaveQuickTransitions() obs_source_t *OBSBasic::FindTransition(const char *name) { - for (int i = 0; i < ui->transitions->count(); i++) { - OBSSource tr = ui->transitions->itemData(i).value(); - if (!tr) - continue; + auto nameToUuid = transitionNameToUuids.find(name); - const char *trName = obs_source_get_name(tr); - if (strcmp(trName, name) == 0) - return tr; + if (nameToUuid != transitionNameToUuids.end()) { + auto transition = transitions.find(nameToUuid->second); + + if (transition == transitions.end()) + return nullptr; + + return transition->second; } return nullptr; @@ -322,7 +333,7 @@ void OBSBasic::TransitionToScene(OBSSource source, bool force, bool quickTransit obs_transition_set(transition, source); OnEvent(OBS_FRONTEND_EVENT_SCENE_CHANGED); } else { - int duration = ui->transitionDuration->value(); + int duration = GetTransitionDuration(); /* check for scene override */ OBSSource trOverride = GetOverrideTransition(source); @@ -377,9 +388,10 @@ void OBSBasic::SetTransition(OBSSource transition) OBSSourceAutoRelease oldTransition = obs_get_output_source(0); if (oldTransition && transition) { + std::string uuid = obs_source_get_uuid(transition); obs_transition_swap_begin(transition, oldTransition); - if (transition != GetCurrentTransition()) - SetComboTransition(ui->transitions, transition); + if (currentTransitionUuid != uuid) + UpdateCurrentTransition(uuid, false); obs_set_output_source(0, transition); obs_transition_swap_end(transition, oldTransition); } else { @@ -399,13 +411,12 @@ void OBSBasic::SetTransition(OBSSource transition) OBSSource OBSBasic::GetCurrentTransition() { - return ui->transitions->currentData().value(); -} + auto transition = transitions.find(currentTransitionUuid); -void OBSBasic::on_transitions_currentIndexChanged(int) -{ - OBSSource transition = GetCurrentTransition(); - SetTransition(transition); + if (transition == transitions.end()) + return nullptr; + + return transition->second; } void OBSBasic::AddTransition(const char *id) @@ -424,6 +435,8 @@ void OBSBasic::AddTransition(const char *id) name, placeHolderText); if (accepted) { + std::string uuid; + if (name.empty()) { OBSMessageBox::warning(this, QTStr("NoNameEntered.Title"), QTStr("NoNameEntered.Text")); AddTransition(id); @@ -440,8 +453,16 @@ void OBSBasic::AddTransition(const char *id) source = obs_source_create_private(id, name.c_str(), NULL); InitTransition(source); - ui->transitions->addItem(QT_UTF8(name.c_str()), QVariant::fromValue(OBSSource(source))); - ui->transitions->setCurrentIndex(ui->transitions->count() - 1); + + uuid = obs_source_get_uuid(source); + transitions.insert({uuid, source}); + transitionNameToUuids.insert({name, uuid}); + transitionUuids.push_back(uuid); + + emit TransitionAdded(QString::fromStdString(name), QString::fromStdString(uuid)); + + UpdateCurrentTransition(uuid, true); + CreatePropertiesWindow(source); obs_source_release(source); @@ -477,13 +498,16 @@ void OBSBasic::on_transitionAdd_clicked() void OBSBasic::on_transitionRemove_clicked() { - OBSSource tr = GetCurrentTransition(); + auto transitionIterator = transitions.find(currentTransitionUuid); + OBSSource tr; + const char *name; - if (!tr || !obs_source_configurable(tr) || !QueryRemoveSource(tr)) + if (transitionIterator == transitions.end()) return; - int idx = ui->transitions->findData(QVariant::fromValue(tr)); - if (idx == -1) + tr = transitionIterator->second; + + if (!tr || !obs_source_configurable(tr) || !QueryRemoveSource(tr)) return; for (size_t i = quickTransitions.size(); i > 0; i--) { @@ -496,7 +520,15 @@ void OBSBasic::on_transitionRemove_clicked() } } - ui->transitions->removeItem(idx); + name = obs_source_get_name(tr); + if (name) + transitionNameToUuids.erase(std::string(name)); + + transitionUuids.erase(std::find(transitionUuids.begin(), transitionUuids.end(), currentTransitionUuid)); + transitions.erase(currentTransitionUuid); + emit TransitionRemoved(QString::fromStdString(currentTransitionUuid)); + + UpdateCurrentTransition(transitionUuids.back(), true); OnEvent(OBS_FRONTEND_EVENT_TRANSITION_LIST_CHANGED); @@ -506,8 +538,10 @@ void OBSBasic::on_transitionRemove_clicked() void OBSBasic::RenameTransition(OBSSource transition) { - string name; - QString placeHolderText = QT_UTF8(obs_source_get_name(transition)); + std::string name; + std::string oldName = obs_source_get_name(transition); + std::string uuid = obs_source_get_uuid(transition); + QString placeHolderText = QString::fromStdString(oldName); obs_source_t *source = nullptr; bool accepted = NameDialog::AskForName(this, QTStr("TransitionNameDlg.Title"), QTStr("TransitionNameDlg.Text"), @@ -530,15 +564,19 @@ void OBSBasic::RenameTransition(OBSSource transition) } obs_source_set_name(transition, name.c_str()); - int idx = ui->transitions->findData(QVariant::fromValue(transition)); - if (idx != -1) { - ui->transitions->setItemText(idx, QT_UTF8(name.c_str())); - OnEvent(OBS_FRONTEND_EVENT_TRANSITION_LIST_CHANGED); + if (transitionNameToUuids.find(oldName) == transitionNameToUuids.end()) + return; - ClearQuickTransitionWidgets(); - RefreshQuickTransitions(); - } + transitionNameToUuids.erase(oldName); + transitionNameToUuids.insert({name, uuid}); + + emit TransitionRenamed(QString::fromStdString(uuid), QString::fromStdString(name)); + + OnEvent(OBS_FRONTEND_EVENT_TRANSITION_LIST_CHANGED); + + ClearQuickTransitionWidgets(); + RefreshQuickTransitions(); } void OBSBasic::on_transitionProps_clicked() @@ -565,11 +603,6 @@ void OBSBasic::on_transitionProps_clicked() menu.exec(QCursor::pos()); } -void OBSBasic::on_transitionDuration_valueChanged() -{ - OnEvent(OBS_FRONTEND_EVENT_TRANSITION_DURATION_CHANGED); -} - QuickTransition *OBSBasic::GetQuickTransition(int id) { for (QuickTransition &qt : quickTransitions) { @@ -743,7 +776,6 @@ QMenu *OBSBasic::CreatePerSceneTransitionMenu() { OBSSource scene = GetCurrentSceneSource(); QMenu *menu = new QMenu(QTStr("TransitionOverride")); - QAction *action; OBSDataAutoRelease data = obs_source_get_private_settings(scene); @@ -760,19 +792,24 @@ QMenu *OBSBasic::CreatePerSceneTransitionMenu() duration->setValue(curDuration); auto setTransition = [this](QAction *action) { - int idx = action->property("transition_index").toInt(); + std::string uuid = action->property("transition_uuid").toString().toStdString(); OBSSource scene = GetCurrentSceneSource(); OBSDataAutoRelease data = obs_source_get_private_settings(scene); + auto transitionIter = transitions.find(uuid); + OBSSource transition; - if (idx == -1) { + if (uuid.empty()) { obs_data_set_string(data, "transition", ""); return; } - OBSSource tr = GetTransitionComboItem(ui->transitions, idx); + if (transitionIter == transitions.end()) + return; - if (tr) { - const char *name = obs_source_get_name(tr); + transition = transitionIter->second; + + if (transition) { + const char *name = obs_source_get_name(transition); obs_data_set_string(data, "transition", name); } }; @@ -786,15 +823,16 @@ QMenu *OBSBasic::CreatePerSceneTransitionMenu() connect(duration, (void(QSpinBox::*)(int)) & QSpinBox::valueChanged, setDuration); - for (int i = -1; i < ui->transitions->count(); i++) { + auto addAction = [&](const std::string &uuid = "") { const char *name = ""; + QAction *action; - if (i >= 0) { - OBSSource tr; - tr = GetTransitionComboItem(ui->transitions, i); - if (!tr) - continue; - name = obs_source_get_name(tr); + if (!uuid.empty()) { + auto transition = transitions.find(uuid); + if (transition == transitions.end()) + return; + + name = obs_source_get_name(transition->second); } bool match = (name && strcmp(name, curTransition) == 0); @@ -803,12 +841,16 @@ QMenu *OBSBasic::CreatePerSceneTransitionMenu() name = Str("None"); action = menu->addAction(QT_UTF8(name)); - action->setProperty("transition_index", i); + action->setProperty("transition_uuid", QString::fromStdString(uuid)); action->setCheckable(true); action->setChecked(match); connect(action, &QAction::triggered, std::bind(setTransition, action)); - } + }; + + addAction(); + for (const auto &[uuid, transition] : transitions) + addAction(uuid); QWidgetAction *durationAction = new QWidgetAction(menu); durationAction->setDefaultWidget(duration); @@ -1014,7 +1056,6 @@ QMenu *OBSBasic::CreateTransitionMenu(QWidget *parent, QuickTransition *qt) { QMenu *menu = new QMenu(parent); QAction *action; - OBSSource tr; if (qt) { action = menu->addAction(QTStr("Remove")); @@ -1038,8 +1079,6 @@ QMenu *OBSBasic::CreateTransitionMenu(QWidget *parent, QuickTransition *qt) &OBSBasic::QuickTransitionChangeDuration); } - tr = fadeTransition; - action = menu->addAction(QTStr("FadeToBlack")); action->setProperty("fadeToBlack", true); @@ -1051,14 +1090,12 @@ QMenu *OBSBasic::CreateTransitionMenu(QWidget *parent, QuickTransition *qt) connect(action, &QAction::triggered, this, &OBSBasic::AddQuickTransition); } - for (int i = 0; i < ui->transitions->count(); i++) { - tr = GetTransitionComboItem(ui->transitions, i); - - if (!tr) + for (const auto &[uuid, transition] : transitions) { + if (!transition) continue; - action = menu->addAction(obs_source_get_name(tr)); - action->setProperty("transition_index", i); + action = menu->addAction(obs_source_get_name(transition)); + action->setProperty("transition_uuid", QString::fromStdString(uuid)); if (qt) { action->setProperty("id", qt->id); @@ -1118,10 +1155,16 @@ void OBSBasic::AddQuickTransitionId(int id) void OBSBasic::AddQuickTransition() { - int trIdx = sender()->property("transition_index").toInt(); + std::string transitionUuid = sender()->property("transition_uuid").toString().toStdString(); QSpinBox *duration = sender()->property("duration").value(); bool fadeToBlack = sender()->property("fadeToBlack").value(); - OBSSource transition = fadeToBlack ? OBSSource(fadeTransition) : GetTransitionComboItem(ui->transitions, trIdx); + auto transitionIter = transitions.find(transitionUuid); + OBSSource transition; + + if (!fadeToBlack && (transitionIter == transitions.end())) + return; + + transition = fadeToBlack ? OBSSource(fadeTransition) : transitionIter->second; if (!transition) return; @@ -1172,12 +1215,19 @@ void OBSBasic::QuickTransitionClicked() void OBSBasic::QuickTransitionChange() { int id = sender()->property("id").toInt(); - int trIdx = sender()->property("transition_index").toInt(); + std::string transitionUuid = sender()->property("transition_uuid").toString().toStdString(); bool fadeToBlack = sender()->property("fadeToBlack").value(); QuickTransition *qt = GetQuickTransition(id); if (qt) { - OBSSource tr = fadeToBlack ? OBSSource(fadeTransition) : GetTransitionComboItem(ui->transitions, trIdx); + auto transitionIter = transitions.find(transitionUuid); + OBSSource tr; + + if (!fadeToBlack && (transitionIter == transitions.end())) + return; + + tr = fadeToBlack ? OBSSource(fadeTransition) : transitionIter->second; + if (tr) { qt->source = tr; qt->fadeToBlack = fadeToBlack; @@ -1280,53 +1330,59 @@ void OBSBasic::EnableTransitionWidgets(bool enable) obs_data_array_t *OBSBasic::SaveTransitions() { - obs_data_array_t *transitions = obs_data_array_create(); + obs_data_array_t *transitionsData = obs_data_array_create(); - for (int i = 0; i < ui->transitions->count(); i++) { - OBSSource tr = ui->transitions->itemData(i).value(); - if (!tr || !obs_source_configurable(tr)) + for (const auto &[uuid, transition] : transitions) { + if (!transition || !obs_source_configurable(transition.Get())) continue; OBSDataAutoRelease sourceData = obs_data_create(); - OBSDataAutoRelease settings = obs_source_get_settings(tr); + OBSDataAutoRelease settings = obs_source_get_settings(transition.Get()); - obs_data_set_string(sourceData, "name", obs_source_get_name(tr)); - obs_data_set_string(sourceData, "id", obs_obj_get_id(tr)); + obs_data_set_string(sourceData, "name", obs_source_get_name(transition.Get())); + obs_data_set_string(sourceData, "id", obs_obj_get_id(transition.Get())); obs_data_set_obj(sourceData, "settings", settings); - obs_data_array_push_back(transitions, sourceData); + obs_data_array_push_back(transitionsData, sourceData); } for (const OBSDataAutoRelease &transition : safeModeTransitions) { - obs_data_array_push_back(transitions, transition); + obs_data_array_push_back(transitionsData, transition); } - return transitions; + return transitionsData; } -void OBSBasic::LoadTransitions(obs_data_array_t *transitions, obs_load_source_cb cb, void *private_data) +void OBSBasic::LoadTransitions(obs_data_array_t *transitionsData, obs_load_source_cb cb, void *private_data) { - size_t count = obs_data_array_count(transitions); + size_t count = obs_data_array_count(transitionsData); safeModeTransitions.clear(); for (size_t i = 0; i < count; i++) { - OBSDataAutoRelease item = obs_data_array_item(transitions, i); + OBSDataAutoRelease item = obs_data_array_item(transitionsData, i); const char *name = obs_data_get_string(item, "name"); const char *id = obs_data_get_string(item, "id"); OBSDataAutoRelease settings = obs_data_get_obj(item, "settings"); OBSSourceAutoRelease source = obs_source_create_private(id, name, settings); if (!obs_obj_invalid(source)) { + std::string uuid = obs_source_get_uuid(source); InitTransition(source); - ui->transitions->addItem(QT_UTF8(name), QVariant::fromValue(OBSSource(source))); - ui->transitions->setCurrentIndex(ui->transitions->count() - 1); + transitions.insert({uuid, OBSSource(source)}); + transitionNameToUuids.insert({name, uuid}); + transitionUuids.push_back(uuid); + + emit TransitionAdded(QT_UTF8(name), QString::fromStdString(uuid)); + if (cb) cb(private_data, source); } else if (safe_mode || disable_3p_plugins) { safeModeTransitions.push_back(std::move(item)); } } + + UpdateCurrentTransition(transitionUuids.back(), true); } OBSSource OBSBasic::GetOverrideTransition(OBSSource source) @@ -1359,5 +1415,48 @@ int OBSBasic::GetOverrideTransitionDuration(OBSSource source) int OBSBasic::GetTransitionDuration() { - return ui->transitionDuration->value(); + return transitionDuration; +} + +void OBSBasic::UpdateCurrentTransition(const std::string &uuid, bool setTransition) +{ + auto transitionIter = transitions.find(uuid); + + if (currentTransitionUuid == uuid || transitionIter == transitions.end()) + return; + + currentTransitionUuid = uuid; + + if (setTransition) + SetTransition(transitionIter->second); + + emit CurrentTransitionChanged(QString::fromStdString(uuid)); +} + +void OBSBasic::SetCurrentTransition(const QString &uuid) +{ + auto transitionIter = transitions.find(uuid.toStdString()); + + if (currentTransitionUuid == uuid.toStdString() || transitionIter == transitions.end()) + return; + + currentTransitionUuid = uuid.toStdString(); + SetTransition(transitionIter->second); + + emit CurrentTransitionChanged(uuid); +} + +void OBSBasic::SetTransitionDuration(int duration) +{ + duration = std::max(duration, 50); + duration = std::min(duration, 20000); + + if (duration == transitionDuration) + return; + + transitionDuration = duration; + + emit TransitionDurationChanged(transitionDuration); + + OnEvent(OBS_FRONTEND_EVENT_TRANSITION_DURATION_CHANGED); }