From 3b1775d97f1a2e92a2d8de38e34613ab90fa04e9 Mon Sep 17 00:00:00 2001 From: jp9000 Date: Sat, 27 Feb 2016 02:50:04 -0800 Subject: [PATCH] UI: Add configurable transitions --- obs/data/locale/en-US.ini | 7 + obs/forms/OBSBasic.ui | 115 ++++++++++++-- obs/window-basic-main-transitions.cpp | 220 +++++++++++++++++++++++++- obs/window-basic-main.cpp | 11 +- obs/window-basic-main.hpp | 6 + 5 files changed, 341 insertions(+), 18 deletions(-) diff --git a/obs/data/locale/en-US.ini b/obs/data/locale/en-US.ini index 014035d81..b3140c932 100644 --- a/obs/data/locale/en-US.ini +++ b/obs/data/locale/en-US.ini @@ -59,10 +59,17 @@ QuickTransitions.EditPropertiesTT="When editing the same scene, allows editing p QuickTransitions.HotkeyName="Quick Transition: %1" # transitions +Basic.AddTransition="Add Configurable Transition" +Basic.RemoveTransition="Remove Configurable Transition" +Basic.TransitionProperties="Transition Properties" Basic.SceneTransitions="Scene Transitions" Basic.TransitionDuration="Duration" Basic.TogglePreviewProgramMode="Studio Mode" +# transition name dialog +TransitionNameDlg.Text="Please enter the name of the transition" +TransitionNameDlg.Title="Transition Name" + # title bar strings TitleBar.Profile="Profile" TitleBar.Scenes="Scenes" diff --git a/obs/forms/OBSBasic.ui b/obs/forms/OBSBasic.ui index fae7d3375..0bd019194 100644 --- a/obs/forms/OBSBasic.ui +++ b/obs/forms/OBSBasic.ui @@ -581,10 +581,103 @@ 4 - - - 2 + + + + 120 + 0 + + + + + + + 4 + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + + 22 + 22 + + + + Basic.AddTransition + + + Basic.AddTransition + + + + + + + :/res/images/add.png:/res/images/add.png + + + true + + + addIconSmall + + + + + + + + 0 + 0 + + + + + 22 + 22 + + + + Basic.RemoveTransition + + + Basic.RemoveTransition + + + + + + + :/res/images/list_remove.png:/res/images/list_remove.png + + + true + + + removeIconSmall + + + @@ -599,6 +692,12 @@ 22 + + Basic.TransitionProperties + + + Basic.TransitionProperties + @@ -614,16 +713,6 @@ - - - - - 120 - 0 - - - - diff --git a/obs/window-basic-main-transitions.cpp b/obs/window-basic-main-transitions.cpp index c2488c0d9..f96261256 100644 --- a/obs/window-basic-main-transitions.cpp +++ b/obs/window-basic-main-transitions.cpp @@ -18,9 +18,11 @@ #include #include #include +#include #include #include "window-basic-main.hpp" #include "display-helpers.hpp" +#include "window-namedialog.hpp" #include "menu-button.hpp" #include "qt-wrappers.hpp" @@ -301,9 +303,6 @@ void OBSBasic::SetTransition(obs_source_t *transition) SetComboTransition(ui->transitions, transition); obs_set_output_source(0, transition); obs_transition_swap_end(transition, oldTransition); - - bool showPropertiesButton = obs_source_configurable(transition); - ui->transitionProps->setVisible(showPropertiesButton); } else { obs_set_output_source(0, transition); } @@ -314,6 +313,10 @@ void OBSBasic::SetTransition(obs_source_t *transition) bool fixed = transition ? obs_transition_fixed(transition) : false; ui->transitionDurationLabel->setVisible(!fixed); ui->transitionDuration->setVisible(!fixed); + + bool configurable = obs_source_configurable(transition); + ui->transitionRemove->setEnabled(configurable); + ui->transitionProps->setEnabled(configurable); } OBSSource OBSBasic::GetCurrentTransition() @@ -327,9 +330,170 @@ void OBSBasic::on_transitions_currentIndexChanged(int) SetTransition(transition); } +void OBSBasic::AddTransition() +{ + QAction *action = reinterpret_cast(sender()); + QString idStr = action->property("id").toString(); + + string name; + QString placeHolderText = QT_UTF8( + obs_source_get_display_name(QT_TO_UTF8(idStr))); + QString format = placeHolderText + " (%1)"; + obs_source_t *source = nullptr; + int i = 1; + + while ((source = FindTransition(QT_TO_UTF8(placeHolderText)))) { + placeHolderText = format.arg(++i); + } + + bool accepted = NameDialog::AskForName(this, + QTStr("TransitionNameDlg.Title"), + QTStr("TransitionNameDlg.Text"), + name, placeHolderText); + + if (accepted) { + if (name.empty()) { + QMessageBox::information(this, + QTStr("NoNameEntered.Title"), + QTStr("NoNameEntered.Text")); + AddTransition(); + return; + } + + source = FindTransition(name.c_str()); + if (source) { + QMessageBox::information(this, + QTStr("NameExists.Title"), + QTStr("NameExists.Text")); + + AddTransition(); + return; + } + + source = obs_source_create_private(QT_TO_UTF8(idStr), + 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); + CreatePropertiesWindow(source); + obs_source_release(source); + } +} + +void OBSBasic::on_transitionAdd_clicked() +{ + bool foundConfigurableTransitions = false; + QMenu menu(this); + size_t idx = 0; + const char *id; + + while (obs_enum_transition_types(idx++, &id)) { + if (obs_is_source_configurable(id)) { + const char *name = obs_source_get_display_name(id); + QAction *action = new QAction(name, this); + action->setProperty("id", id); + + connect(action, SIGNAL(triggered()), + this, SLOT(AddTransition())); + + menu.addAction(action); + foundConfigurableTransitions = true; + } + } + + if (foundConfigurableTransitions) + menu.exec(QCursor::pos()); +} + +void OBSBasic::on_transitionRemove_clicked() +{ + OBSSource tr = GetCurrentTransition(); + + if (!tr || !obs_source_configurable(tr) || !QueryRemoveSource(tr)) + return; + + int idx = ui->transitions->findData(QVariant::fromValue(tr)); + if (idx == -1) + return; + + for (size_t i = quickTransitions.size(); i > 0; i--) { + QuickTransition &qt = quickTransitions[i - 1]; + if (qt.source == tr) { + if (qt.button) + qt.button->deleteLater(); + RemoveQuickTransitionHotkey(&qt); + quickTransitions.erase(quickTransitions.begin() + i - 1); + } + } + + ui->transitions->removeItem(idx); +} + +void OBSBasic::RenameTransition() +{ + QAction *action = reinterpret_cast(sender()); + QVariant variant = action->property("transition"); + obs_source_t *transition = variant.value(); + + string name; + QString placeHolderText = QT_UTF8(obs_source_get_name(transition)); + obs_source_t *source = nullptr; + + bool accepted = NameDialog::AskForName(this, + QTStr("TransitionNameDlg.Title"), + QTStr("TransitionNameDlg.Text"), + name, placeHolderText); + + if (accepted) { + if (name.empty()) { + QMessageBox::information(this, + QTStr("NoNameEntered.Title"), + QTStr("NoNameEntered.Text")); + RenameTransition(); + return; + } + + source = FindTransition(name.c_str()); + if (source) { + QMessageBox::information(this, + QTStr("NameExists.Title"), + QTStr("NameExists.Text")); + + RenameTransition(); + return; + } + + obs_source_set_name(transition, name.c_str()); + int idx = ui->transitions->findData(variant); + if (idx != -1) + ui->transitions->setItemText(idx, QT_UTF8(name.c_str())); + } +} + void OBSBasic::on_transitionProps_clicked() { - // TODO + OBSSource source = GetCurrentTransition(); + + if (!obs_source_configurable(source)) + return; + + auto properties = [&] () { + CreatePropertiesWindow(source); + }; + + QMenu menu(this); + + QAction *action = new QAction(QTStr("Rename"), this); + connect(action, SIGNAL(triggered()), this, SLOT(RenameTransition())); + action->setProperty("transition", QVariant::fromValue(source)); + menu.addAction(action); + + action = new QAction(QTStr("Properties"), this); + connect(action, &QAction::triggered, properties); + menu.addAction(action); + + menu.exec(QCursor::pos()); } QuickTransition *OBSBasic::GetQuickTransition(int id) @@ -875,3 +1039,51 @@ void OBSBasic::ResizeProgram(uint32_t cx, uint32_t cy) programX += float(PREVIEW_EDGE_SIZE); programY += float(PREVIEW_EDGE_SIZE); } + +obs_data_array_t *OBSBasic::SaveTransitions() +{ + obs_data_array_t *transitions = obs_data_array_create(); + + for (int i = 0; i < ui->transitions->count(); i++) { + OBSSource tr = ui->transitions->itemData(i).value(); + if (!obs_source_configurable(tr)) + continue; + + obs_data_t *sourceData = obs_data_create(); + obs_data_t *settings = obs_source_get_settings(tr); + + 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_obj(sourceData, "settings", settings); + + obs_data_array_push_back(transitions, sourceData); + + obs_data_release(settings); + obs_data_release(sourceData); + } + + return transitions; +} + +void OBSBasic::LoadTransitions(obs_data_array_t *transitions) +{ + size_t count = obs_data_array_count(transitions); + + for (size_t i = 0; i < count; i++) { + obs_data_t *item = obs_data_array_item(transitions, i); + const char *name = obs_data_get_string(item, "name"); + const char *id = obs_data_get_string(item, "id"); + obs_data_t *settings = obs_data_get_obj(item, "settings"); + + obs_source_t *source = obs_source_create_private(id, name, + settings); + InitTransition(source); + ui->transitions->addItem(QT_UTF8(name), + QVariant::fromValue(OBSSource(source))); + ui->transitions->setCurrentIndex(ui->transitions->count() - 1); + obs_source_release(source); + + obs_data_release(settings); + obs_data_release(item); + } +} diff --git a/obs/window-basic-main.cpp b/obs/window-basic-main.cpp index 78d8954f3..ce205056a 100644 --- a/obs/window-basic-main.cpp +++ b/obs/window-basic-main.cpp @@ -247,6 +247,7 @@ static void SaveAudioDevice(const char *name, int channel, obs_data_t *parent, static obs_data_t *GenerateSaveData(obs_data_array_t *sceneOrder, obs_data_array_t *quickTransitionData, int transitionDuration, + obs_data_array_t *transitions, OBSScene &scene, OBSSource &curProgramScene) { obs_data_t *saveData = obs_data_create(); @@ -287,6 +288,7 @@ static obs_data_t *GenerateSaveData(obs_data_array_t *sceneOrder, obs_data_set_string(saveData, "name", sceneCollection); obs_data_set_array(saveData, "sources", sourcesArray); obs_data_set_array(saveData, "quick_transitions", quickTransitionData); + obs_data_set_array(saveData, "transitions", transitions); obs_data_array_release(sourcesArray); obs_data_set_string(saveData, "current_transition", @@ -352,9 +354,10 @@ void OBSBasic::Save(const char *file) curProgramScene = obs_scene_get_source(scene); obs_data_array_t *sceneOrder = SaveSceneListOrder(); + obs_data_array_t *transitions = SaveTransitions(); obs_data_array_t *quickTrData = SaveQuickTransitions(); obs_data_t *saveData = GenerateSaveData(sceneOrder, quickTrData, - ui->transitionDuration->value(), + ui->transitionDuration->value(), transitions, scene, curProgramScene); if (!obs_data_save_json_safe(saveData, file, "tmp", "bak")) @@ -363,6 +366,7 @@ void OBSBasic::Save(const char *file) obs_data_release(saveData); obs_data_array_release(sceneOrder); obs_data_array_release(quickTrData); + obs_data_array_release(transitions); } static void LoadAudioDevice(const char *name, int channel, obs_data_t *parent) @@ -488,6 +492,7 @@ void OBSBasic::Load(const char *file) obs_data_array_t *sceneOrder = obs_data_get_array(data, "scene_order"); obs_data_array_t *sources = obs_data_get_array(data, "sources"); + obs_data_array_t *transitions= obs_data_get_array(data, "transitions"); const char *sceneName = obs_data_get_string(data, "current_scene"); const char *programSceneName = obs_data_get_string(data, @@ -523,9 +528,13 @@ void OBSBasic::Load(const char *file) obs_load_sources(sources); + if (transitions) + LoadTransitions(transitions); if (sceneOrder) LoadSceneListOrder(sceneOrder); + obs_data_array_release(transitions); + curTransition = FindTransition(transitionName); if (!curTransition) curTransition = fadeTransition; diff --git a/obs/window-basic-main.hpp b/obs/window-basic-main.hpp index 8dc646864..7ccf1a262 100644 --- a/obs/window-basic-main.hpp +++ b/obs/window-basic-main.hpp @@ -220,6 +220,8 @@ private: obs_source_t *FindTransition(const char *name); void SetTransition(obs_source_t *transition); OBSSource GetCurrentTransition(); + obs_data_array_t *SaveTransitions(); + void LoadTransitions(obs_data_array_t *transitions); obs_source_t *fadeTransition; @@ -314,6 +316,8 @@ private slots: void ProcessHotkey(obs_hotkey_id id, bool pressed); + void AddTransition(); + void RenameTransition(); void TransitionClicked(); void TransitionStopped(); void TriggerQuickTransition(int id); @@ -465,6 +469,8 @@ private slots: void on_actionAlwaysOnTop_triggered(); void on_transitions_currentIndexChanged(int index); + void on_transitionAdd_clicked(); + void on_transitionRemove_clicked(); void on_transitionProps_clicked(); void on_modeSwitch_clicked();