From 018809a0d2c0d303185d64e02a2ae6024846f630 Mon Sep 17 00:00:00 2001 From: tytan652 Date: Sun, 9 Feb 2025 15:21:19 +0100 Subject: [PATCH 1/4] frontend: Remove some shadowing --- .../widgets/OBSBasic_SceneCollections.cpp | 10 +++++----- frontend/widgets/OBSBasic_StudioMode.cpp | 10 +++++----- frontend/widgets/OBSBasic_Transitions.cpp | 20 +++++++++---------- 3 files changed, 20 insertions(+), 20 deletions(-) diff --git a/frontend/widgets/OBSBasic_SceneCollections.cpp b/frontend/widgets/OBSBasic_SceneCollections.cpp index 88a4a6e6e5a678..0c0857af86e02e 100644 --- a/frontend/widgets/OBSBasic_SceneCollections.cpp +++ b/frontend/widgets/OBSBasic_SceneCollections.cpp @@ -882,12 +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, + transitionsData, scene, curProgramScene, savedProjectorList, savedCanvases); obs_data_set_bool(saveData, "preview_locked", ui->preview->Locked()); @@ -1172,7 +1172,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 +1295,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); 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..27e3be25a0fe4e 100644 --- a/frontend/widgets/OBSBasic_Transitions.cpp +++ b/frontend/widgets/OBSBasic_Transitions.cpp @@ -47,7 +47,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 +59,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,7 +68,7 @@ void OBSBasic::InitDefaultTransitions() } } - for (OBSSource &tr : transitions) { + for (OBSSource &tr : defaultTransitions) { ui->transitions->addItem(QT_UTF8(obs_source_get_name(tr)), QVariant::fromValue(OBSSource(tr))); } } @@ -1280,7 +1280,7 @@ 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(); @@ -1294,23 +1294,23 @@ obs_data_array_t *OBSBasic::SaveTransitions() obs_data_set_string(sourceData, "id", obs_obj_get_id(tr)); 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"); From b8ccce10cc8e4bb8c81adc6c744716f4b41028ef Mon Sep 17 00:00:00 2001 From: tytan652 Date: Sat, 8 Feb 2025 11:06:30 +0100 Subject: [PATCH 2/4] frontend: Replace transitions combobox direct access --- frontend/OBSStudioAPI.cpp | 11 +- frontend/widgets/OBSBasic.cpp | 28 ++- frontend/widgets/OBSBasic.hpp | 20 +- .../widgets/OBSBasic_SceneCollections.cpp | 7 +- frontend/widgets/OBSBasic_Transitions.cpp | 193 +++++++++++++----- 5 files changed, 202 insertions(+), 57 deletions(-) diff --git a/frontend/OBSStudioAPI.cpp b/frontend/OBSStudioAPI.cpp index 7c710d0144de07..283cd2f4820aa2 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); } } diff --git a/frontend/widgets/OBSBasic.cpp b/frontend/widgets/OBSBasic.cpp index ffe42be300fd2e..6e08f900c32636 100644 --- a/frontend/widgets/OBSBasic.cpp +++ b/frontend/widgets/OBSBasic.cpp @@ -287,6 +287,32 @@ 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()); }); + startingDockLayout = saveState(); statsDock = new OBSDock(); @@ -967,7 +993,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..98ab86dc5ebf02 100644 --- a/frontend/widgets/OBSBasic.hpp +++ b/frontend/widgets/OBSBasic.hpp @@ -1466,6 +1466,13 @@ private slots: std::vector safeModeTransitions; QPointer transitionButton; QPointer perSceneTransitionMenu; + + std::unordered_map transitions; + // TODO: Reduce usages of an index to identify a transition + std::vector transitionUuids; + // FIXME: Replace usages of a name to identify a transition + std::unordered_map transitionNameToUuids; + std::string currentTransitionUuid; obs_source_t *fadeTransition; obs_source_t *cutTransition; std::vector quickTransitions; @@ -1519,6 +1526,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 +1537,8 @@ 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); + private slots: void AddTransition(const char *id); void RenameTransition(OBSSource transition); @@ -1540,7 +1551,6 @@ 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(); @@ -1549,6 +1559,14 @@ private slots: 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); + public: int GetTransitionDuration(); int GetTbarPosition(); diff --git a/frontend/widgets/OBSBasic_SceneCollections.cpp b/frontend/widgets/OBSBasic_SceneCollections.cpp index 0c0857af86e02e..60a9eb0156612c 100644 --- a/frontend/widgets/OBSBasic_SceneCollections.cpp +++ b/frontend/widgets/OBSBasic_SceneCollections.cpp @@ -1499,7 +1499,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_Transitions.cpp b/frontend/widgets/OBSBasic_Transitions.cpp index 27e3be25a0fe4e..882e8a7e76cf98 100644 --- a/frontend/widgets/OBSBasic_Transitions.cpp +++ b/frontend/widgets/OBSBasic_Transitions.cpp @@ -17,6 +17,8 @@ #include "OBSBasic.hpp" +#include + #include #include #include @@ -69,8 +71,16 @@ void OBSBasic::InitDefaultTransitions() } for (OBSSource &tr : defaultTransitions) { - ui->transitions->addItem(QT_UTF8(obs_source_get_name(tr)), QVariant::fromValue(OBSSource(tr))); + 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); + + if (nameToUuid != transitionNameToUuids.end()) { + auto transition = transitions.find(nameToUuid->second); - const char *trName = obs_source_get_name(tr); - if (strcmp(trName, name) == 0) - return tr; + if (transition == transitions.end()) + return nullptr; + + return transition->second; } return nullptr; @@ -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() @@ -763,13 +801,17 @@ QMenu *OBSBasic::CreatePerSceneTransitionMenu() int idx = action->property("transition_index").toInt(); OBSSource scene = GetCurrentSceneSource(); OBSDataAutoRelease data = obs_source_get_private_settings(scene); + auto transitionIter = transitions.find(transitionUuids[idx]); if (idx == -1) { obs_data_set_string(data, "transition", ""); return; } - OBSSource tr = GetTransitionComboItem(ui->transitions, idx); + if (transitionIter == transitions.end()) + return; + + OBSSource tr = transitionIter->second; if (tr) { const char *name = obs_source_get_name(tr); @@ -786,12 +828,17 @@ QMenu *OBSBasic::CreatePerSceneTransitionMenu() connect(duration, (void(QSpinBox::*)(int)) & QSpinBox::valueChanged, setDuration); - for (int i = -1; i < ui->transitions->count(); i++) { + for (int i = -1; i < (int)transitionUuids.size(); i++) { const char *name = ""; if (i >= 0) { + auto transitionIter = transitions.find(transitionUuids[i]); OBSSource tr; - tr = GetTransitionComboItem(ui->transitions, i); + + if (transitionIter == transitions.end()) + continue; + + tr = transitionIter->second; if (!tr) continue; name = obs_source_get_name(tr); @@ -1051,8 +1098,13 @@ 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); + for (int i = 0; i < (int)transitionUuids.size(); i++) { + auto transitionIter = transitions.find(transitionUuids[i]); + + if (transitionIter == transitions.end()) + continue; + + tr = transitionIter->second; if (!tr) continue; @@ -1121,7 +1173,13 @@ void OBSBasic::AddQuickTransition() int trIdx = sender()->property("transition_index").toInt(); 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(transitionUuids[trIdx]); + OBSSource transition; + + if (!fadeToBlack && (transitionIter == transitions.end())) + return; + + transition = fadeToBlack ? OBSSource(fadeTransition) : transitionIter->second; if (!transition) return; @@ -1177,7 +1235,14 @@ void OBSBasic::QuickTransitionChange() QuickTransition *qt = GetQuickTransition(id); if (qt) { - OBSSource tr = fadeToBlack ? OBSSource(fadeTransition) : GetTransitionComboItem(ui->transitions, trIdx); + auto transitionIter = transitions.find(transitionUuids[trIdx]); + OBSSource tr; + + if (!fadeToBlack && (transitionIter == transitions.end())) + return; + + tr = fadeToBlack ? OBSSource(fadeTransition) : transitionIter->second; + if (tr) { qt->source = tr; qt->fadeToBlack = fadeToBlack; @@ -1282,16 +1347,15 @@ obs_data_array_t *OBSBasic::SaveTransitions() { 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(transitionsData, sourceData); @@ -1317,16 +1381,23 @@ void OBSBasic::LoadTransitions(obs_data_array_t *transitionsData, obs_load_sourc 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) @@ -1361,3 +1432,31 @@ int OBSBasic::GetTransitionDuration() { return ui->transitionDuration->value(); } + +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); +} From cc6a525c647ca583f930d13ed5e31934524e4c03 Mon Sep 17 00:00:00 2001 From: tytan652 Date: Tue, 18 Feb 2025 16:07:03 +0100 Subject: [PATCH 3/4] frontend: Reduce reliance on transition indexes --- frontend/widgets/OBSBasic.hpp | 2 +- frontend/widgets/OBSBasic_Transitions.cpp | 66 ++++++++++------------- 2 files changed, 29 insertions(+), 39 deletions(-) diff --git a/frontend/widgets/OBSBasic.hpp b/frontend/widgets/OBSBasic.hpp index 98ab86dc5ebf02..44b5d8b3ede203 100644 --- a/frontend/widgets/OBSBasic.hpp +++ b/frontend/widgets/OBSBasic.hpp @@ -1468,7 +1468,7 @@ private slots: QPointer perSceneTransitionMenu; std::unordered_map transitions; - // TODO: Reduce usages of an index to identify a transition + // 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; diff --git a/frontend/widgets/OBSBasic_Transitions.cpp b/frontend/widgets/OBSBasic_Transitions.cpp index 882e8a7e76cf98..e19fc9d17fb21c 100644 --- a/frontend/widgets/OBSBasic_Transitions.cpp +++ b/frontend/widgets/OBSBasic_Transitions.cpp @@ -781,7 +781,6 @@ QMenu *OBSBasic::CreatePerSceneTransitionMenu() { OBSSource scene = GetCurrentSceneSource(); QMenu *menu = new QMenu(QTStr("TransitionOverride")); - QAction *action; OBSDataAutoRelease data = obs_source_get_private_settings(scene); @@ -798,12 +797,13 @@ 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(transitionUuids[idx]); + auto transitionIter = transitions.find(uuid); + OBSSource transition; - if (idx == -1) { + if (uuid.empty()) { obs_data_set_string(data, "transition", ""); return; } @@ -811,10 +811,10 @@ QMenu *OBSBasic::CreatePerSceneTransitionMenu() if (transitionIter == transitions.end()) return; - OBSSource tr = transitionIter->second; + transition = transitionIter->second; - if (tr) { - const char *name = obs_source_get_name(tr); + if (transition) { + const char *name = obs_source_get_name(transition); obs_data_set_string(data, "transition", name); } }; @@ -828,20 +828,16 @@ QMenu *OBSBasic::CreatePerSceneTransitionMenu() connect(duration, (void(QSpinBox::*)(int)) & QSpinBox::valueChanged, setDuration); - for (int i = -1; i < (int)transitionUuids.size(); i++) { + auto addAction = [&](const std::string &uuid = "") { const char *name = ""; + QAction *action; - if (i >= 0) { - auto transitionIter = transitions.find(transitionUuids[i]); - OBSSource tr; - - if (transitionIter == transitions.end()) - continue; + if (!uuid.empty()) { + auto transition = transitions.find(uuid); + if (transition == transitions.end()) + return; - tr = transitionIter->second; - if (!tr) - continue; - name = obs_source_get_name(tr); + name = obs_source_get_name(transition->second); } bool match = (name && strcmp(name, curTransition) == 0); @@ -850,12 +846,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); @@ -1061,7 +1061,6 @@ QMenu *OBSBasic::CreateTransitionMenu(QWidget *parent, QuickTransition *qt) { QMenu *menu = new QMenu(parent); QAction *action; - OBSSource tr; if (qt) { action = menu->addAction(QTStr("Remove")); @@ -1085,8 +1084,6 @@ QMenu *OBSBasic::CreateTransitionMenu(QWidget *parent, QuickTransition *qt) &OBSBasic::QuickTransitionChangeDuration); } - tr = fadeTransition; - action = menu->addAction(QTStr("FadeToBlack")); action->setProperty("fadeToBlack", true); @@ -1098,19 +1095,12 @@ QMenu *OBSBasic::CreateTransitionMenu(QWidget *parent, QuickTransition *qt) connect(action, &QAction::triggered, this, &OBSBasic::AddQuickTransition); } - for (int i = 0; i < (int)transitionUuids.size(); i++) { - auto transitionIter = transitions.find(transitionUuids[i]); - - if (transitionIter == transitions.end()) - continue; - - tr = transitionIter->second; - - 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); @@ -1170,10 +1160,10 @@ 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(); - auto transitionIter = transitions.find(transitionUuids[trIdx]); + auto transitionIter = transitions.find(transitionUuid); OBSSource transition; if (!fadeToBlack && (transitionIter == transitions.end())) @@ -1230,12 +1220,12 @@ 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) { - auto transitionIter = transitions.find(transitionUuids[trIdx]); + auto transitionIter = transitions.find(transitionUuid); OBSSource tr; if (!fadeToBlack && (transitionIter == transitions.end())) From 29903d01099bb103f2f8d1938e0ece77f6c35ab0 Mon Sep 17 00:00:00 2001 From: tytan652 Date: Tue, 18 Feb 2025 17:25:00 +0100 Subject: [PATCH 4/4] frontend: Replace transition duration spinbox direct access --- frontend/OBSStudioAPI.cpp | 4 ++-- frontend/widgets/OBSBasic.cpp | 8 +++++++ frontend/widgets/OBSBasic.hpp | 6 ++++- .../widgets/OBSBasic_SceneCollections.cpp | 9 ++++--- frontend/widgets/OBSBasic_Transitions.cpp | 24 +++++++++++++------ 5 files changed, 36 insertions(+), 15 deletions(-) diff --git a/frontend/OBSStudioAPI.cpp b/frontend/OBSStudioAPI.cpp index 283cd2f4820aa2..e9cbf7a8353537 100644 --- a/frontend/OBSStudioAPI.cpp +++ b/frontend/OBSStudioAPI.cpp @@ -95,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 6e08f900c32636..b5af325b0e36ae 100644 --- a/frontend/widgets/OBSBasic.cpp +++ b/frontend/widgets/OBSBasic.cpp @@ -313,6 +313,14 @@ OBSBasic::OBSBasic(QWidget *parent) : OBSMainWindow(parent), undo_s(ui), ui(new 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(); diff --git a/frontend/widgets/OBSBasic.hpp b/frontend/widgets/OBSBasic.hpp index 44b5d8b3ede203..75d74f045ea3ab 100644 --- a/frontend/widgets/OBSBasic.hpp +++ b/frontend/widgets/OBSBasic.hpp @@ -1472,6 +1472,7 @@ private slots: 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; @@ -1539,6 +1540,8 @@ public slots: void SetCurrentTransition(const QString &uuid); + void SetTransitionDuration(int duration); + private slots: void AddTransition(const char *id); void RenameTransition(OBSSource transition); @@ -1554,7 +1557,6 @@ private slots: void on_transitionAdd_clicked(); void on_transitionRemove_clicked(); void on_transitionProps_clicked(); - void on_transitionDuration_valueChanged(); void ShowTransitionProperties(); void HideTransitionProperties(); @@ -1567,6 +1569,8 @@ private slots: 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 60a9eb0156612c..ee9e4e6086f735 100644 --- a/frontend/widgets/OBSBasic_SceneCollections.cpp +++ b/frontend/widgets/OBSBasic_SceneCollections.cpp @@ -886,9 +886,8 @@ void OBSBasic::Save(SceneCollection &collection) OBSDataArrayAutoRelease quickTrData = SaveQuickTransitions(); OBSDataArrayAutoRelease savedProjectorList = SaveProjectors(); OBSDataArrayAutoRelease savedCanvases = OBS::Canvas::SaveCanvases(canvases); - OBSDataAutoRelease saveData = GenerateSaveData(sceneOrder, quickTrData, ui->transitionDuration->value(), - transitionsData, 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); @@ -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: diff --git a/frontend/widgets/OBSBasic_Transitions.cpp b/frontend/widgets/OBSBasic_Transitions.cpp index e19fc9d17fb21c..b76f433762fc73 100644 --- a/frontend/widgets/OBSBasic_Transitions.cpp +++ b/frontend/widgets/OBSBasic_Transitions.cpp @@ -333,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); @@ -603,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) { @@ -1420,7 +1415,7 @@ int OBSBasic::GetOverrideTransitionDuration(OBSSource source) int OBSBasic::GetTransitionDuration() { - return ui->transitionDuration->value(); + return transitionDuration; } void OBSBasic::UpdateCurrentTransition(const std::string &uuid, bool setTransition) @@ -1450,3 +1445,18 @@ void OBSBasic::SetCurrentTransition(const QString &uuid) 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); +}