From 13896331600ad9aca7e39b38cbbd4bba42839d87 Mon Sep 17 00:00:00 2001 From: Clayton Groeneveld Date: Thu, 14 May 2020 00:58:18 -0500 Subject: [PATCH 1/3] libobs: Add dsk scene type --- libobs/obs-scene.c | 50 +++++++++++++++++++++++++++++++++++++++++++++ libobs/obs-scene.h | 2 ++ libobs/obs-source.c | 2 ++ libobs/obs.c | 2 ++ libobs/obs.h | 8 ++++++++ 5 files changed, 64 insertions(+) diff --git a/libobs/obs-scene.c b/libobs/obs-scene.c index d5f52373ae8606..a64b67835af323 100644 --- a/libobs/obs-scene.c +++ b/libobs/obs-scene.c @@ -21,6 +21,7 @@ #include "obs-scene.h" const struct obs_source_info group_info; +const struct obs_source_info dsk_info; static void resize_group(obs_sceneitem_t *group); static void resize_scene(obs_scene_t *scene); @@ -80,6 +81,12 @@ static const char *group_getname(void *unused) return "Group"; } +static const char *dsk_getname(void *unused) +{ + UNUSED_PARAMETER(unused); + return "DSK"; +} + static void *scene_create(obs_data_t *settings, struct obs_source *source) { pthread_mutexattr_t attr; @@ -91,6 +98,8 @@ static void *scene_create(obs_data_t *settings, struct obs_source *source) scene->custom_size = true; scene->cx = 0; scene->cy = 0; + } else if (strcmp(source->info.id, dsk_info.id) == 0) { + scene->is_dsk = true; } signal_handler_add_array(obs_source_get_signal_handler(source), @@ -1198,6 +1207,24 @@ const struct obs_source_info group_info = { .enum_active_sources = scene_enum_active_sources, .enum_all_sources = scene_enum_all_sources}; +const struct obs_source_info dsk_info = { + .id = "dsk", + .type = OBS_SOURCE_TYPE_SCENE, + .output_flags = OBS_SOURCE_VIDEO | OBS_SOURCE_CUSTOM_DRAW | + OBS_SOURCE_COMPOSITE, + .get_name = dsk_getname, + .create = scene_create, + .destroy = scene_destroy, + .video_tick = scene_video_tick, + .video_render = scene_video_render, + .audio_render = scene_audio_render, + .get_width = scene_getwidth, + .get_height = scene_getheight, + .load = scene_load, + .save = scene_save, + .enum_active_sources = scene_enum_active_sources, + .enum_all_sources = scene_enum_all_sources}; + static inline obs_scene_t *create_id(const char *id, const char *name) { struct obs_source *source = obs_source_create(id, name, NULL, NULL); @@ -1220,6 +1247,11 @@ obs_scene_t *obs_scene_create_private(const char *name) return create_private_id("scene", name); } +obs_scene_t *obs_dsk_create(const char *name) +{ + return create_id("dsk", name); +} + static obs_source_t *get_child_at_idx(obs_scene_t *scene, size_t idx) { struct obs_scene_item *item = scene->first_item; @@ -1424,6 +1456,14 @@ obs_scene_t *obs_group_from_source(const obs_source_t *source) return source->context.data; } +obs_scene_t *obs_dsk_from_source(const obs_source_t *source) +{ + if (!source || strcmp(source->info.id, dsk_info.id) != 0) + return NULL; + + return source->context.data; +} + obs_sceneitem_t *obs_scene_find_source(obs_scene_t *scene, const char *name) { struct obs_scene_item *item; @@ -2994,6 +3034,16 @@ bool obs_scene_is_group(const obs_scene_t *scene) return scene ? scene->is_group : false; } +bool obs_source_is_dsk(const obs_source_t *source) +{ + return source && strcmp(source->info.id, dsk_info.id) == 0; +} + +bool obs_scene_is_dsk(const obs_scene_t *scene) +{ + return scene ? scene->is_dsk : false; +} + void obs_sceneitem_group_enum_items(obs_sceneitem_t *group, bool (*callback)(obs_scene_t *, obs_sceneitem_t *, void *), diff --git a/libobs/obs-scene.h b/libobs/obs-scene.h index a9ddf740994373..4349deecea9483 100644 --- a/libobs/obs-scene.h +++ b/libobs/obs-scene.h @@ -97,4 +97,6 @@ struct obs_scene { pthread_mutex_t video_mutex; pthread_mutex_t audio_mutex; struct obs_scene_item *first_item; + + bool is_dsk; }; diff --git a/libobs/obs-source.c b/libobs/obs-source.c index 615faa502bb1cc..2b974d832168af 100644 --- a/libobs/obs-source.c +++ b/libobs/obs-source.c @@ -509,6 +509,8 @@ obs_source_t *obs_source_duplicate(obs_source_t *source, const char *new_name, } if (!scene) scene = obs_group_from_source(source); + if (!scene) + scene = obs_dsk_from_source(source); if (!scene) return NULL; diff --git a/libobs/obs.c b/libobs/obs.c index b4815951fcd73e..be8d24dce933df 100644 --- a/libobs/obs.c +++ b/libobs/obs.c @@ -823,6 +823,7 @@ static inline void obs_free_hotkeys(void) extern const struct obs_source_info scene_info; extern const struct obs_source_info group_info; +extern const struct obs_source_info dsk_info; static const char *submix_name(void *unused) { @@ -870,6 +871,7 @@ static bool obs_init(const char *locale, const char *module_config_path, obs->locale = bstrdup(locale); obs_register_source(&scene_info); obs_register_source(&group_info); + obs_register_source(&dsk_info); obs_register_source(&audio_line_info); add_default_module_paths(); return true; diff --git a/libobs/obs.h b/libobs/obs.h index d091ebebf09d70..469a796080b748 100644 --- a/libobs/obs.h +++ b/libobs/obs.h @@ -1455,6 +1455,8 @@ EXPORT void obs_transition_swap_end(obs_source_t *tr_dest, */ EXPORT obs_scene_t *obs_scene_create(const char *name); +EXPORT obs_scene_t *obs_dsk_create(const char *name); + EXPORT obs_scene_t *obs_scene_create_private(const char *name); enum obs_scene_duplicate_type { @@ -1658,6 +1660,12 @@ EXPORT obs_scene_t *obs_group_from_source(const obs_source_t *source); EXPORT void obs_sceneitem_defer_group_resize_begin(obs_sceneitem_t *item); EXPORT void obs_sceneitem_defer_group_resize_end(obs_sceneitem_t *item); +/** Gets the dsk from its source, or NULL if not a group */ +EXPORT obs_scene_t *obs_dsk_from_source(const obs_source_t *source); + +EXPORT bool obs_source_is_dsk(const obs_source_t *source); +EXPORT bool obs_scene_is_dsk(const obs_scene_t *scene); + /* ------------------------------------------------------------------------- */ /* Outputs */ From 4467750751bcc42027b0c9b38012b5baae3afc74 Mon Sep 17 00:00:00 2001 From: Clayton Groeneveld Date: Thu, 14 May 2020 00:59:41 -0500 Subject: [PATCH 2/3] frontend-api: Add frontend dsk functions --- UI/api-interface.cpp | 32 +++++++++++++++++-- UI/obs-frontend-api/obs-frontend-api.cpp | 18 +++++++++++ UI/obs-frontend-api/obs-frontend-api.h | 7 ++++ UI/obs-frontend-api/obs-frontend-internal.hpp | 6 ++++ 4 files changed, 61 insertions(+), 2 deletions(-) diff --git a/UI/api-interface.cpp b/UI/api-interface.cpp index a2139efa4ed93d..a71756fdc4f422 100644 --- a/UI/api-interface.cpp +++ b/UI/api-interface.cpp @@ -92,7 +92,7 @@ struct OBSStudioAPI : obs_frontend_callbacks { if (main->IsPreviewProgramMode()) { source = obs_weak_source_get_source(main->programScene); } else { - source = main->GetCurrentSceneSource(); + source = main->GetCurrentSceneListSource(); obs_source_addref(source); } return source; @@ -505,7 +505,7 @@ struct OBSStudioAPI : obs_frontend_callbacks { OBSSource source = nullptr; if (main->IsPreviewProgramMode()) { - source = main->GetCurrentSceneSource(); + source = main->GetCurrentSceneListSource(); obs_source_addref(source); } @@ -523,6 +523,34 @@ struct OBSStudioAPI : obs_frontend_callbacks { } } + obs_source_t *obs_frontend_get_current_global_scene(void) override + { + OBSSource source = main->GetCurrentSceneListSource(); + obs_source_addref(source); + + return source; + } + + void obs_frontend_set_current_global_scene(obs_source_t *scene) override + { + QMetaObject::invokeMethod(main, "SetDSKSource", + Q_ARG(OBSSource, OBSSource(scene)), + Q_ARG(bool, false)); + } + + void obs_frontend_get_global_scenes( + struct obs_frontend_source_list *sources) override + { + for (int i = 0; i < main->ui->dsk->count(); i++) { + QListWidgetItem *item = main->ui->dsk->item(i); + OBSScene scene = GetOBSRef(item); + obs_source_t *source = obs_scene_get_source(scene); + + obs_source_addref(source); + da_push_back(sources->sources, &source); + } + } + void on_load(obs_data_t *settings) override { for (size_t i = saveCallbacks.size(); i > 0; i--) { diff --git a/UI/obs-frontend-api/obs-frontend-api.cpp b/UI/obs-frontend-api/obs-frontend-api.cpp index 8066666c529f65..fa694a99b36bd7 100644 --- a/UI/obs-frontend-api/obs-frontend-api.cpp +++ b/UI/obs-frontend-api/obs-frontend-api.cpp @@ -451,3 +451,21 @@ void obs_frontend_set_current_preview_scene(obs_source_t *scene) if (callbacks_valid()) c->obs_frontend_set_current_preview_scene(scene); } + +obs_source_t *obs_frontend_get_current_global_scene(void) +{ + return !!callbacks_valid() ? c->obs_frontend_get_current_global_scene() + : nullptr; +} + +void obs_frontend_set_current_global_scene(obs_source_t *scene) +{ + if (callbacks_valid()) + c->obs_frontend_set_current_global_scene(scene); +} + +void obs_frontend_get_global_scenes(struct obs_frontend_source_list *sources) +{ + if (callbacks_valid()) + c->obs_frontend_get_global_scenes(sources); +} diff --git a/UI/obs-frontend-api/obs-frontend-api.h b/UI/obs-frontend-api/obs-frontend-api.h index 18e9d4e1c02c38..e738b6a5079dee 100644 --- a/UI/obs-frontend-api/obs-frontend-api.h +++ b/UI/obs-frontend-api/obs-frontend-api.h @@ -49,6 +49,8 @@ enum obs_frontend_event { OBS_FRONTEND_EVENT_RECORDING_UNPAUSED, OBS_FRONTEND_EVENT_TRANSITION_DURATION_CHANGED, + + OBS_FRONTEND_EVENT_GLOBAL_SCENE_CHANGED, }; /* ------------------------------------------------------------------------- */ @@ -192,6 +194,11 @@ EXPORT bool obs_frontend_preview_enabled(void); EXPORT obs_source_t *obs_frontend_get_current_preview_scene(void); EXPORT void obs_frontend_set_current_preview_scene(obs_source_t *scene); +EXPORT void +obs_frontend_get_global_scenes(struct obs_frontend_source_list *sources); +EXPORT obs_source_t *obs_frontend_get_current_global_scene(void); +EXPORT void obs_frontend_set_current_global_scene(obs_source_t *scene); + /* ------------------------------------------------------------------------- */ #ifdef __cplusplus diff --git a/UI/obs-frontend-api/obs-frontend-internal.hpp b/UI/obs-frontend-api/obs-frontend-internal.hpp index a5ba063b92840a..46ea0384c2c85b 100644 --- a/UI/obs-frontend-api/obs-frontend-internal.hpp +++ b/UI/obs-frontend-api/obs-frontend-internal.hpp @@ -116,6 +116,12 @@ struct obs_frontend_callbacks { virtual void on_preload(obs_data_t *settings) = 0; virtual void on_save(obs_data_t *settings) = 0; virtual void on_event(enum obs_frontend_event event) = 0; + + virtual void obs_frontend_get_global_scenes( + struct obs_frontend_source_list *sources) = 0; + virtual obs_source_t *obs_frontend_get_current_global_scene(void) = 0; + virtual void + obs_frontend_set_current_global_scene(obs_source_t *scene) = 0; }; EXPORT void From b81e86a0306e803fe17b9c19198249cbd0e5a40e Mon Sep 17 00:00:00 2001 From: Clayton Groeneveld Date: Thu, 14 May 2020 01:00:13 -0500 Subject: [PATCH 3/3] UI: Add global scenes dock --- UI/CMakeLists.txt | 3 +- UI/data/locale/en-US.ini | 1 + UI/forms/OBSBasic.ui | 214 +++++++++++++- UI/source-tree.cpp | 36 ++- UI/source-tree.hpp | 5 + UI/window-basic-main-dsk.cpp | 137 +++++++++ UI/window-basic-main-transitions.cpp | 18 +- UI/window-basic-main.cpp | 403 +++++++++++++++++++++------ UI/window-basic-main.hpp | 41 ++- UI/window-basic-source-select.cpp | 2 +- UI/window-projector.cpp | 6 +- 11 files changed, 756 insertions(+), 110 deletions(-) create mode 100644 UI/window-basic-main-dsk.cpp diff --git a/UI/CMakeLists.txt b/UI/CMakeLists.txt index bf1fc8e6d4e3ea..3078b444d4cf8a 100644 --- a/UI/CMakeLists.txt +++ b/UI/CMakeLists.txt @@ -262,7 +262,8 @@ set(obs_SOURCES source-label.cpp remote-text.cpp audio-encoders.cpp - qt-wrappers.cpp) + qt-wrappers.cpp + window-basic-main-dsk.cpp) set(obs_HEADERS ${obs_PLATFORM_HEADERS} diff --git a/UI/data/locale/en-US.ini b/UI/data/locale/en-US.ini index 017987c90faffb..66c51e50c2284a 100644 --- a/UI/data/locale/en-US.ini +++ b/UI/data/locale/en-US.ini @@ -94,6 +94,7 @@ Windowed="Windowed" Percent="Percent" AspectRatio="Aspect Ratio %1:%2" LockVolume="Lock Volume" +DownstreamKeyer="Global Scenes" # warning if program already open AlreadyRunning.Title="OBS is already running" diff --git a/UI/forms/OBSBasic.ui b/UI/forms/OBSBasic.ui index d1dd30827c1251..c3ec05fd369591 100644 --- a/UI/forms/OBSBasic.ui +++ b/UI/forms/OBSBasic.ui @@ -7,7 +7,7 @@ 0 0 - 1079 + 1191 730 @@ -202,8 +202,8 @@ 0 0 - 1079 - 22 + 1191 + 30 @@ -395,6 +395,7 @@ + @@ -429,6 +430,144 @@ + + + false + + + QDockWidget::AllDockWidgetFeatures + + + DownstreamKeyer + + + 8 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + 160 + 0 + + + + QFrame::StyledPanel + + + QFrame::Sunken + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + Qt::CustomContextMenu + + + QFrame::NoFrame + + + QFrame::Plain + + + true + + + true + + + QAbstractItemView::InternalMove + + + Qt::TargetMoveAction + + + + + + + + + 16 + 16 + + + + false + + + + + + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 150 + 0 + + + + + + + + + + QDockWidget::AllDockWidgetFeatures @@ -753,7 +892,7 @@ 0 0 - 71 + 76 16 @@ -807,7 +946,7 @@ 0 0 16 - 28 + 26 @@ -1241,6 +1380,18 @@ addIconSmall + + + + :/res/images/add.png:/res/images/add.png + + + Add + + + addIconSmall + + @@ -1271,6 +1422,24 @@ removeIconSmall + + + + :/res/images/list_remove.png:/res/images/list_remove.png + + + Remove + + + Del + + + Qt::WidgetWithChildrenShortcut + + + removeIconSmall + + @@ -1316,6 +1485,18 @@ upArrowIconSmall + + + + :/res/images/up.png:/res/images/up.png + + + MoveUp + + + upArrowIconSmall + + true @@ -1343,6 +1524,18 @@ downArrowIconSmall + + + + :/res/images/down.png:/res/images/down.png + + + MoveDown + + + downArrowIconSmall + + true @@ -1824,6 +2017,17 @@ Basic.MainMenu.View.SourceIcons + + + true + + + true + + + DownstreamKeyer + + diff --git a/UI/source-tree.cpp b/UI/source-tree.cpp index fdd4c37c888016..4e3ef73f7f86d4 100644 --- a/UI/source-tree.cpp +++ b/UI/source-tree.cpp @@ -526,10 +526,14 @@ void SourceTreeItem::Deselect() void SourceTreeModel::OBSFrontendEvent(enum obs_frontend_event event, void *ptr) { SourceTreeModel *stm = reinterpret_cast(ptr); + OBSBasic *main = reinterpret_cast(App()->GetMainWindow()); switch ((int)event) { case OBS_FRONTEND_EVENT_PREVIEW_SCENE_CHANGED: - stm->SceneChanged(); + stm->st->SetScene(main->GetCurrentSceneListScene()); + break; + case OBS_FRONTEND_EVENT_GLOBAL_SCENE_CHANGED: + stm->st->SetScene(main->GetCurrentGlobalScene()); break; case OBS_FRONTEND_EVENT_EXIT: case OBS_FRONTEND_EVENT_SCENE_COLLECTION_CLEANUP: @@ -540,6 +544,7 @@ void SourceTreeModel::OBSFrontendEvent(enum obs_frontend_event event, void *ptr) void SourceTreeModel::Clear() { + st->scene = nullptr; beginResetModel(); items.clear(); endResetModel(); @@ -1557,3 +1562,32 @@ void SourceTree::paintEvent(QPaintEvent *event) QListView::paintEvent(event); } } + +void SourceTree::SetScene(OBSScene newScene) +{ + OBSBasic *main = reinterpret_cast(App()->GetMainWindow()); + + if ((obs_scene_is_dsk(newScene) && !main->IsDSKDockVisible()) || + !main->ui->dsk->count()) + scene = main->GetCurrentSceneListScene(); + else + scene = newScene; + + QString title = QTStr("Basic.Main.Sources"); + + if (scene) { + const char *name = + obs_source_get_name(obs_scene_get_source(scene)); + title += " ("; + title += QT_UTF8(name); + title += ")"; + } + + main->ui->sourcesDock->setWindowTitle(title); + GetStm()->SceneChanged(); +} + +obs_scene_t *SourceTree::GetScene() +{ + return scene; +} diff --git a/UI/source-tree.hpp b/UI/source-tree.hpp index 32a4c5929a83bb..ffa93a6357a717 100644 --- a/UI/source-tree.hpp +++ b/UI/source-tree.hpp @@ -159,6 +159,8 @@ class SourceTree : public QListView { return reinterpret_cast(model()); } + obs_scene_t *scene = nullptr; + public: inline SourceTreeItem *GetItemWidget(int idx) { @@ -184,6 +186,9 @@ class SourceTree : public QListView { void UpdateIcons(); void SetIconsVisible(bool visible); + obs_scene_t *GetScene(); + void SetScene(OBSScene newScene); + public slots: inline void ReorderItems() { GetStm()->ReorderItems(); } inline void RefreshItems() { GetStm()->SceneChanged(); } diff --git a/UI/window-basic-main-dsk.cpp b/UI/window-basic-main-dsk.cpp new file mode 100644 index 00000000000000..5dd666cf00e279 --- /dev/null +++ b/UI/window-basic-main-dsk.cpp @@ -0,0 +1,137 @@ +#include "window-basic-main.hpp" +#include "qt-wrappers.hpp" + +void OBSBasic::on_actionAddSceneDSK_triggered() +{ + AddSceneQuery(true); +} + +void OBSBasic::on_actionRemoveSceneDSK_triggered() +{ + on_actionRemoveScene_triggered(); +} + +void OBSBasic::on_actionSceneUpDSK_triggered() +{ + ChangeListIndex(ui->dsk, true, -1, 0); +} + +void OBSBasic::on_actionSceneDownDSK_triggered() +{ + ChangeListIndex(ui->dsk, true, 1, ui->dsk->count() - 1); +} + +void OBSBasic::MoveDSKToTop() +{ + ChangeListIndex(ui->dsk, false, 0, 0); +} + +void OBSBasic::MoveDSKToBottom() +{ + ChangeListIndex(ui->dsk, false, ui->dsk->count() - 1, + ui->dsk->count() - 1); +} + +void OBSBasic::on_dsk_customContextMenuRequested(const QPoint &pos) +{ + ShowScenesMenu(ui->dsk, pos); +} + +void OBSBasic::SetDSKSource(OBSSource source) +{ + OBSSource prevSource = obs_get_output_source(7); + obs_source_release(prevSource); + + if (prevSource == source) + return; + + obs_set_output_source(7, source); + + for (int i = 0; i < ui->dsk->count(); i++) { + QListWidgetItem *item = ui->dsk->item(i); + OBSScene scene = GetSceneFromListItem(item); + + if (obs_dsk_from_source(source) == scene) { + ui->dsk->blockSignals(true); + ui->dsk->setCurrentItem(item); + ui->dsk->blockSignals(false); + break; + } + } + + if (api) + api->on_event(OBS_FRONTEND_EVENT_GLOBAL_SCENE_CHANGED); + + blog(LOG_INFO, "Switched to global scene '%s'", + obs_source_get_name(source)); +} + +void OBSBasic::on_actionGridModeDSK_triggered() +{ + bool gridMode = !ui->dsk->GetGridMode(); + ui->dsk->SetGridMode(gridMode); +} + +obs_data_array_t *OBSBasic::SaveDSKListOrder() +{ + obs_data_array_t *dskOrder = obs_data_array_create(); + + for (int i = 0; i < ui->dsk->count(); i++) { + obs_data_t *data = obs_data_create(); + obs_data_set_string(data, "name", + QT_TO_UTF8(ui->dsk->item(i)->text())); + obs_data_array_push_back(dskOrder, data); + obs_data_release(data); + } + + return dskOrder; +} + +void OBSBasic::LoadDSKListOrder(obs_data_array_t *array) +{ + size_t num = obs_data_array_count(array); + + for (size_t i = 0; i < num; i++) { + obs_data_t *data = obs_data_array_item(array, i); + const char *name = obs_data_get_string(data, "name"); + + ReorderItemByName(ui->dsk, name, (int)i); + + obs_data_release(data); + } +} + +void OBSBasic::on_dsk_currentItemChanged(QListWidgetItem *current, + QListWidgetItem *prev) +{ + on_dsk_itemClicked(current); + UNUSED_PARAMETER(prev); +} + +void OBSBasic::on_dsk_itemClicked(QListWidgetItem *item) +{ + if (!item) + return; + + ui->dsk->blockSignals(true); + OBSScene scene = GetCurrentGlobalScene(); + ui->sources->SetScene(scene); + + SetDSKSource(obs_scene_get_source(scene)); + ui->dsk->blockSignals(false); +} + +void OBSBasic::EditDSKName() +{ + QListWidgetItem *item = ui->dsk->currentItem(); + Qt::ItemFlags flags = item->flags(); + + item->setFlags(flags | Qt::ItemIsEditable); + ui->dsk->editItem(item); + item->setFlags(flags); +} + +bool OBSBasic::IsDSKDockVisible() +{ + return ui->dskDock->isVisible(); +} diff --git a/UI/window-basic-main-transitions.cpp b/UI/window-basic-main-transitions.cpp index ea2d9a8f601cad..f1faca1fa43d86 100644 --- a/UI/window-basic-main-transitions.cpp +++ b/UI/window-basic-main-transitions.cpp @@ -123,7 +123,7 @@ void OBSBasic::TriggerQuickTransition(int id) QuickTransition *qt = GetQuickTransition(id); if (qt && previewProgramMode) { - OBSScene scene = GetCurrentScene(); + OBSScene scene = GetCurrentSceneListScene(); obs_source_t *source = obs_scene_get_source(scene); if (GetCurrentTransition() != qt->source) { @@ -322,7 +322,7 @@ void OBSBasic::TransitionToScene(OBSSource source, bool force, if (!sceneDuplicationMode && newScene == source) return; - if (newScene && newScene != GetCurrentSceneSource()) + if (newScene && newScene != GetCurrentSceneListSource()) swapScene = lastProgramScene; } } @@ -675,6 +675,9 @@ template static T GetOBSRef(QListWidgetItem *item) void OBSBasic::SetCurrentScene(OBSSource scene, bool force) { + if (prevSceneListSource == scene) + return; + if (!IsPreviewProgramMode()) { TransitionToScene(scene, force); } else { @@ -688,7 +691,7 @@ void OBSBasic::SetCurrentScene(OBSSource scene, bool force) } } - if (obs_scene_get_source(GetCurrentScene()) != scene) { + if (GetCurrentSceneListSource() != scene) { for (int i = 0; i < ui->scenes->count(); i++) { QListWidgetItem *item = ui->scenes->item(i); OBSScene itemScene = GetOBSRef(item); @@ -707,6 +710,7 @@ void OBSBasic::SetCurrentScene(OBSSource scene, bool force) } UpdateSceneSelection(scene); + prevSceneListSource = scene; bool userSwitched = (!force && !disableSaving); blog(LOG_INFO, "%s to scene '%s'", @@ -748,7 +752,7 @@ void OBSBasic::CreateProgramDisplay() void OBSBasic::TransitionClicked() { if (previewProgramMode) - TransitionToScene(GetCurrentScene()); + TransitionToScene(GetCurrentSceneListScene()); } #define T_BAR_PRECISION 1024 @@ -916,7 +920,7 @@ void OBSBasic::TBarChanged(int value) obs_source_release(transition); if (!tBarActive) { - OBSSource sceneSource = GetCurrentSceneSource(); + OBSSource sceneSource = GetCurrentSceneListSource(); OBSSource tBarTr = GetOverrideTransition(sceneSource); if (!ValidTBarTransition(tBarTr)) { @@ -1315,7 +1319,7 @@ void OBSBasic::SetPreviewProgramMode(bool enabled) CreateProgramDisplay(); CreateProgramOptions(); - OBSScene curScene = GetCurrentScene(); + OBSScene curScene = GetCurrentSceneListScene(); obs_scene_t *dup; if (sceneDuplicationMode) { @@ -1382,7 +1386,7 @@ void OBSBasic::SetPreviewProgramMode(bool enabled) } else { OBSSource actualProgramScene = OBSGetStrongRef(programScene); if (!actualProgramScene) - actualProgramScene = GetCurrentSceneSource(); + actualProgramScene = GetCurrentSceneListSource(); else SetCurrentScene(actualProgramScene, true); TransitionToScene(actualProgramScene, true); diff --git a/UI/window-basic-main.cpp b/UI/window-basic-main.cpp index 09304eb156352e..ff8e3389bc2ddd 100644 --- a/UI/window-basic-main.cpp +++ b/UI/window-basic-main.cpp @@ -254,12 +254,17 @@ OBSBasic::OBSBasic(QWidget *parent) ui->scenes->setAttribute(Qt::WA_MacShowFocusRect, false); ui->sources->setAttribute(Qt::WA_MacShowFocusRect, false); + ui->dsk->setAttribute(Qt::WA_MacShowFocusRect, false); bool sceneGrid = config_get_bool(App()->GlobalConfig(), "BasicWindow", "gridMode"); ui->scenes->SetGridMode(sceneGrid); + bool dskGrid = config_get_bool(App()->GlobalConfig(), "BasicWindow", + "gridModeDSK"); + ui->dsk->SetGridMode(dskGrid); ui->scenes->setItemDelegate(new SceneRenameDelegate(ui->scenes)); + ui->dsk->setItemDelegate(new SceneRenameDelegate(ui->dsk)); auto displayResize = [this]() { struct obs_video_info ovi; @@ -288,6 +293,12 @@ OBSBasic::OBSBasic(QWidget *parent) this, SLOT(SceneNameEdited(QWidget *, QAbstractItemDelegate::EndEditHint))); + connect(ui->dsk->itemDelegate(), + SIGNAL(closeEditor(QWidget *, + QAbstractItemDelegate::EndEditHint)), + this, + SLOT(SceneNameEditedDSK(QWidget *, + QAbstractItemDelegate::EndEditHint))); cpuUsageInfo = os_cpu_usage_info_start(); cpuUsageTimer = new QTimer(this); @@ -310,9 +321,15 @@ OBSBasic::OBSBasic(QWidget *parent) SLOT(EditSceneItemName())); ui->sourcesDock->addAction(renameSource); + QAction *renameDSK = new QAction(ui->dskDock); + renameDSK->setShortcutContext(Qt::WidgetWithChildrenShortcut); + connect(renameDSK, SIGNAL(triggered()), this, SLOT(EditDSKName())); + ui->dskDock->addAction(renameDSK); + #ifdef __APPLE__ renameScene->setShortcut({Qt::Key_Return}); renameSource->setShortcut({Qt::Key_Return}); + renameDSK->setShortcut({Qt::Key_Return}); ui->actionRemoveSource->setShortcuts({Qt::Key_Backspace}); ui->actionRemoveScene->setShortcuts({Qt::Key_Backspace}); @@ -322,6 +339,7 @@ OBSBasic::OBSBasic(QWidget *parent) #else renameScene->setShortcut({Qt::Key_F2}); renameSource->setShortcut({Qt::Key_F2}); + renameDSK->setShortcut({Qt::Key_F2}); #endif auto addNudge = [this](const QKeySequence &seq, const char *s) { @@ -343,6 +361,7 @@ OBSBasic::OBSBasic(QWidget *parent) assignDockToggle(ui->transitionsDock, ui->toggleTransitions); assignDockToggle(ui->controlsDock, ui->toggleControls); assignDockToggle(statsDock, ui->toggleStats); + assignDockToggle(ui->dskDock, ui->toggleDSK); //hide all docking panes ui->toggleScenes->setChecked(false); @@ -351,6 +370,7 @@ OBSBasic::OBSBasic(QWidget *parent) ui->toggleTransitions->setChecked(false); ui->toggleControls->setChecked(false); ui->toggleStats->setChecked(false); + ui->toggleDSK->setChecked(false); QPoint curPos; @@ -431,7 +451,8 @@ static obs_data_t *GenerateSaveData(obs_data_array_t *sceneOrder, int transitionDuration, obs_data_array_t *transitions, OBSScene &scene, OBSSource &curProgramScene, - obs_data_array_t *savedProjectorList) + obs_data_array_t *savedProjectorList, + obs_data_array_t *dskOrder) { obs_data_t *saveData = obs_data_create(); @@ -449,7 +470,7 @@ static obs_data_t *GenerateSaveData(obs_data_array_t *sceneOrder, /* save non-group sources */ auto FilterAudioSources = [&](obs_source_t *source) { - if (obs_source_is_group(source)) + if (obs_source_is_group(source) || obs_source_is_dsk(source)) return false; return find(begin(audioSources), end(audioSources), source) == @@ -474,6 +495,16 @@ static obs_data_t *GenerateSaveData(obs_data_array_t *sceneOrder, }, nullptr); + /* -------------------------------- */ + /* save dsk sources separately */ + + /* saving separately ensures they won't be loaded in older versions */ + obs_data_array_t *dskArray = obs_save_sources_filtered( + [](void *, obs_source_t *source) { + return obs_source_is_dsk(source); + }, + nullptr); + /* -------------------------------- */ obs_source_t *transition = obs_get_output_source(0); @@ -481,20 +512,28 @@ static obs_data_t *GenerateSaveData(obs_data_array_t *sceneOrder, const char *sceneName = obs_source_get_name(currentScene); const char *programName = obs_source_get_name(curProgramScene); + obs_source_t *dsk = obs_get_output_source(7); + obs_source_release(dsk); + const char *sceneCollection = config_get_string( App()->GlobalConfig(), "Basic", "SceneCollection"); obs_data_set_string(saveData, "current_scene", sceneName); + obs_data_set_string(saveData, "current_dsk_scene", + obs_source_get_name(dsk)); obs_data_set_string(saveData, "current_program_scene", programName); obs_data_set_array(saveData, "scene_order", sceneOrder); + obs_data_set_array(saveData, "dsk_order", dskOrder); obs_data_set_string(saveData, "name", sceneCollection); obs_data_set_array(saveData, "sources", sourcesArray); obs_data_set_array(saveData, "groups", groupsArray); + obs_data_set_array(saveData, "dsk_scenes", dskArray); obs_data_set_array(saveData, "quick_transitions", quickTransitionData); obs_data_set_array(saveData, "transitions", transitions); obs_data_set_array(saveData, "saved_projectors", savedProjectorList); obs_data_array_release(sourcesArray); obs_data_array_release(groupsArray); + obs_data_array_release(dskArray); obs_data_set_string(saveData, "current_transition", obs_source_get_name(transition)); @@ -522,6 +561,14 @@ void OBSBasic::copyActionsDynamicProperties() temp->setProperty(y, x->property(y)); } } + + for (QAction *x : ui->dskToolbar->actions()) { + QWidget *temp = ui->dskToolbar->widgetForAction(x); + + for (QByteArray &y : x->dynamicPropertyNames()) { + temp->setProperty(y, x->property(y)); + } + } } void OBSBasic::UpdateVolumeControlsDecayRate() @@ -618,18 +665,21 @@ obs_data_array_t *OBSBasic::SaveProjectors() void OBSBasic::Save(const char *file) { - OBSScene scene = GetCurrentScene(); + OBSScene scene = GetCurrentSceneListScene(); OBSSource curProgramScene = OBSGetStrongRef(programScene); if (!curProgramScene) curProgramScene = obs_scene_get_source(scene); obs_data_array_t *sceneOrder = SaveSceneListOrder(); + obs_data_array_t *dskOrder = SaveDSKListOrder(); obs_data_array_t *transitions = SaveTransitions(); obs_data_array_t *quickTrData = SaveQuickTransitions(); obs_data_array_t *savedProjectorList = SaveProjectors(); - obs_data_t *saveData = GenerateSaveData( - sceneOrder, quickTrData, ui->transitionDuration->value(), - transitions, scene, curProgramScene, savedProjectorList); + obs_data_t *saveData = GenerateSaveData(sceneOrder, quickTrData, + ui->transitionDuration->value(), + transitions, scene, + curProgramScene, + savedProjectorList, dskOrder); obs_data_set_bool(saveData, "preview_locked", ui->preview->Locked()); obs_data_set_bool(saveData, "scaling_enabled", @@ -656,6 +706,7 @@ void OBSBasic::Save(const char *file) obs_data_array_release(quickTrData); obs_data_array_release(transitions); obs_data_array_release(savedProjectorList); + obs_data_array_release(dskOrder); } void OBSBasic::DeferSaveBegin() @@ -744,7 +795,8 @@ void OBSBasic::CreateDefaultScene(bool firstStart) disableSaving--; } -static void ReorderItemByName(QListWidget *lw, const char *name, int newIndex) +void OBSBasic::ReorderItemByName(QListWidget *lw, const char *name, + int newIndex) { for (int i = 0; i < lw->count(); i++) { QListWidgetItem *item = lw->item(i); @@ -864,6 +916,26 @@ void OBSBasic::LogScenes() blog(LOG_INFO, "------------------------------------------------"); } +void OBSBasic::LogGlobalScenes() +{ + blog(LOG_INFO, "------------------------------------------------"); + blog(LOG_INFO, "Loaded global scenes:"); + + for (int i = 0; i < ui->scenes->count(); i++) { + QListWidgetItem *item = ui->dsk->item(i); + OBSScene scene = GetSceneFromListItem(item); + + obs_source_t *source = obs_scene_get_source(scene); + const char *name = obs_source_get_name(source); + + blog(LOG_INFO, "- global scene '%s':", name); + obs_scene_enum_items(scene, LogSceneItem, (void *)(intptr_t)1); + obs_source_enum_filters(source, LogFilter, (void *)(intptr_t)1); + } + + blog(LOG_INFO, "------------------------------------------------"); +} + void OBSBasic::Load(const char *file) { disableSaving++; @@ -894,6 +966,10 @@ void OBSBasic::Load(const char *file) const char *transitionName = obs_data_get_string(data, "current_transition"); + obs_data_array_t *dskOrder = obs_data_get_array(data, "dsk_order"); + obs_data_array_t *dsk_scenes = obs_data_get_array(data, "dsk_scenes"); + const char *dskName = obs_data_get_string(data, "current_dsk_scene"); + if (!opt_starting_scene.empty()) { programSceneName = opt_starting_scene.c_str(); if (!IsPreviewProgramMode()) @@ -916,6 +992,7 @@ void OBSBasic::Load(const char *file) obs_source_t *curScene; obs_source_t *curProgramScene; obs_source_t *curTransition; + obs_source_t *curDSKSource; if (!name || !*name) name = curSceneCollection; @@ -934,12 +1011,16 @@ void OBSBasic::Load(const char *file) obs_data_array_push_back_array(sources, groups); } + obs_data_array_push_back_array(sources, dsk_scenes); + obs_load_sources(sources, nullptr, nullptr); if (transitions) LoadTransitions(transitions); if (sceneOrder) LoadSceneListOrder(sceneOrder); + if (dskOrder) + LoadDSKListOrder(dskOrder); obs_data_array_release(transitions); @@ -953,6 +1034,7 @@ void OBSBasic::Load(const char *file) retryScene: curScene = obs_get_source_by_name(sceneName); curProgramScene = obs_get_source_by_name(programSceneName); + curDSKSource = obs_get_source_by_name(dskName); /* if the starting scene command line parameter is bad at all, * fall back to original settings */ @@ -971,15 +1053,20 @@ void OBSBasic::Load(const char *file) obs_source_addref(curScene); } + SetDSKSource(curDSKSource); SetCurrentScene(curScene, true); + if (IsPreviewProgramMode()) TransitionToScene(curProgramScene, true); obs_source_release(curScene); obs_source_release(curProgramScene); + obs_source_release(curDSKSource); obs_data_array_release(sources); obs_data_array_release(groups); + obs_data_array_release(dsk_scenes); obs_data_array_release(sceneOrder); + obs_data_array_release(dskOrder); /* ------------------- */ @@ -1034,6 +1121,8 @@ void OBSBasic::Load(const char *file) ui->preview->SetFixedScaling(fixedScaling); emit ui->preview->DisplayResized(); + ui->sources->SetScene(GetCurrentSceneListScene()); + /* ---------------------- */ if (api) @@ -1070,6 +1159,7 @@ void OBSBasic::Load(const char *file) copyFiltersString = nullptr; LogScenes(); + LogGlobalScenes(); disableSaving--; @@ -1832,6 +1922,7 @@ void OBSBasic::OBSInit() SLOT(OpenMultiviewWindow())); ui->sources->UpdateIcons(); + ui->sources->SetScene(GetCurrentSceneListScene()); if (!opt_studio_mode) { SetPreviewProgramMode(config_get_bool(App()->GlobalConfig(), @@ -2480,11 +2571,35 @@ OBSSource OBSBasic::GetProgramSource() } OBSScene OBSBasic::GetCurrentScene() +{ + if (!ui->dsk->count()) + return GetCurrentSceneListScene(); + + return ui->sources->GetScene(); +} + +OBSScene OBSBasic::GetCurrentGlobalScene() +{ + QListWidgetItem *item = ui->dsk->currentItem(); + return item ? GetOBSRef(item) : nullptr; +} + +OBSScene OBSBasic::GetCurrentSceneListScene() { QListWidgetItem *item = ui->scenes->currentItem(); return item ? GetOBSRef(item) : nullptr; } +OBSSource OBSBasic::GetCurrentSceneListSource() +{ + return obs_scene_get_source(GetCurrentSceneListScene()); +} + +OBSScene OBSBasic::GetSceneFromListItem(QListWidgetItem *item) +{ + return item ? GetOBSRef(item) : nullptr; +} + OBSSceneItem OBSBasic::GetSceneItem(QListWidgetItem *item) { return item ? GetOBSRef(item) : nullptr; @@ -2552,10 +2667,23 @@ void OBSBasic::AddScene(OBSSource source) { const char *name = obs_source_get_name(source); obs_scene_t *scene = obs_scene_from_source(source); + bool dsk = false; + + if (!scene) { + scene = obs_dsk_from_source(source); + dsk = true; + } + + if (!scene) + return; QListWidgetItem *item = new QListWidgetItem(QT_UTF8(name)); SetOBSRef(item, OBSScene(scene)); - ui->scenes->addItem(item); + + if (dsk) + ui->dsk->addItem(item); + else + ui->scenes->addItem(item); obs_hotkey_register_source( source, "OBSBasic.SelectScene", @@ -2567,8 +2695,12 @@ void OBSBasic::AddScene(OBSSource source) auto potential_source = static_cast(data); auto source = obs_source_get_ref(potential_source); - if (source && pressed) - main->SetCurrentScene(source); + if (source && pressed) { + if (obs_dsk_from_source(source)) + main->SetDSKSource(source); + else if (obs_scene_from_source(source)) + main->SetCurrentScene(source); + } obs_source_release(source); }, static_cast(source)); @@ -2625,15 +2757,12 @@ void OBSBasic::AddScene(OBSSource source) api->on_event(OBS_FRONTEND_EVENT_SCENE_LIST_CHANGED); } -void OBSBasic::RemoveScene(OBSSource source) +void OBSBasic::RemoveSceneListItem(QListWidget *list, obs_scene_t *scene) { - obs_scene_t *scene = obs_scene_from_source(source); - QListWidgetItem *sel = nullptr; - int count = ui->scenes->count(); - for (int i = 0; i < count; i++) { - auto item = ui->scenes->item(i); + for (int i = 0; i < list->count(); i++) { + auto item = list->item(i); auto cur_scene = GetOBSRef(item); if (cur_scene != scene) continue; @@ -2643,10 +2772,23 @@ void OBSBasic::RemoveScene(OBSSource source) } if (sel != nullptr) { - if (sel == ui->scenes->currentItem()) + if (sel == list->currentItem()) ui->sources->Clear(); delete sel; } +} + +void OBSBasic::RemoveScene(OBSSource source) +{ + obs_scene_t *scene = obs_scene_from_source(source); + + if (!scene) + scene = obs_dsk_from_source(source); + if (!scene) + return; + + RemoveSceneListItem(ui->scenes, scene); + RemoveSceneListItem(ui->dsk, scene); SaveProject(); @@ -2709,9 +2851,9 @@ void OBSBasic::UpdateSceneSelection(OBSSource source) ui->scenes->findItems(QT_UTF8(name), Qt::MatchExactly); if (items.count()) { - sceneChanging = true; + ui->scenes->blockSignals(true); ui->scenes->setCurrentItem(items.first()); - sceneChanging = false; + ui->scenes->blockSignals(false); OBSScene curScene = GetOBSRef(ui->scenes->currentItem()); @@ -2736,6 +2878,7 @@ void OBSBasic::RenameSources(OBSSource source, QString newName, QString prevName) { RenameListValues(ui->scenes, newName, prevName); + RenameListValues(ui->dsk, newName, prevName); for (size_t i = 0; i < volumes.size(); i++) { if (volumes[i]->GetName().compare(prevName) == 0) @@ -3148,7 +3291,7 @@ void OBSBasic::DeactivateAudioSource(OBSSource source) bool OBSBasic::QueryRemoveSource(obs_source_t *source) { if (obs_source_get_type(source) == OBS_SOURCE_TYPE_SCENE && - !obs_source_is_group(source)) { + !obs_source_is_group(source) && !obs_source_is_dsk(source)) { int count = ui->scenes->count(); if (count == 1) { @@ -3392,20 +3535,17 @@ void OBSBasic::SourceCreated(void *data, calldata_t *params) { obs_source_t *source = (obs_source_t *)calldata_ptr(params, "source"); - if (obs_scene_from_source(source) != NULL) - QMetaObject::invokeMethod(static_cast(data), - "AddScene", WaitConnection(), - Q_ARG(OBSSource, OBSSource(source))); + QMetaObject::invokeMethod(static_cast(data), "AddScene", + WaitConnection(), + Q_ARG(OBSSource, OBSSource(source))); } void OBSBasic::SourceRemoved(void *data, calldata_t *params) { obs_source_t *source = (obs_source_t *)calldata_ptr(params, "source"); - if (obs_scene_from_source(source) != NULL) - QMetaObject::invokeMethod(static_cast(data), - "RemoveScene", - Q_ARG(OBSSource, OBSSource(source))); + QMetaObject::invokeMethod(static_cast(data), "RemoveScene", + Q_ARG(OBSSource, OBSSource(source))); } void OBSBasic::SourceActivated(void *data, calldata_t *params) @@ -3532,10 +3672,15 @@ void OBSBasic::RenderMain(void *data, uint32_t cx, uint32_t cy) window->DrawBackdrop(float(ovi.base_width), float(ovi.base_height)); - OBSScene scene = window->GetCurrentScene(); + OBSScene scene = window->GetCurrentSceneListScene(); obs_source_t *source = obs_scene_get_source(scene); if (source) obs_source_video_render(source); + + scene = window->GetCurrentGlobalScene(); + source = obs_scene_get_source(scene); + if (source) + obs_source_video_render(source); } else { obs_render_main_texture_src_color_only(); } @@ -3895,6 +4040,7 @@ void OBSBasic::ClearSceneData() ClearVolumeControls(); ClearListItems(ui->scenes); + ClearListItems(ui->dsk); ui->sources->Clear(); ClearQuickTransitions(); ui->transitions->clear(); @@ -3912,6 +4058,8 @@ void OBSBasic::ClearSceneData() obs_set_output_source(3, nullptr); obs_set_output_source(4, nullptr); obs_set_output_source(5, nullptr); + obs_set_output_source(6, nullptr); + obs_set_output_source(7, nullptr); lastScene = nullptr; swapScene = nullptr; programScene = nullptr; @@ -4141,24 +4289,21 @@ void OBSBasic::on_advAudioProps_destroyed() void OBSBasic::on_scenes_currentItemChanged(QListWidgetItem *current, QListWidgetItem *prev) { - obs_source_t *source = NULL; + on_scenes_itemClicked(current); + UNUSED_PARAMETER(prev); +} - if (sceneChanging) +void OBSBasic::on_scenes_itemClicked(QListWidgetItem *item) +{ + if (!item) return; - if (current) { - obs_scene_t *scene; - - scene = GetOBSRef(current); - source = obs_scene_get_source(scene); - } - - SetCurrentScene(source); - - if (api) - api->on_event(OBS_FRONTEND_EVENT_PREVIEW_SCENE_CHANGED); + ui->scenes->blockSignals(true); + OBSScene scene = GetCurrentSceneListScene(); + ui->sources->SetScene(scene); - UNUSED_PARAMETER(prev); + SetCurrentScene(scene); + ui->scenes->blockSignals(false); } void OBSBasic::EditSceneName() @@ -4214,15 +4359,23 @@ void OBSBasic::AddProjectorMenuMonitors(QMenu *parent, QObject *target, } } -void OBSBasic::on_scenes_customContextMenuRequested(const QPoint &pos) +void OBSBasic::ShowScenesMenu(SceneTree *list, const QPoint &pos) { - QListWidgetItem *item = ui->scenes->itemAt(pos); + QListWidgetItem *item = list->itemAt(pos); + bool dsk = false; + + if (list == ui->dsk) + dsk = true; QMenu popup(this); QMenu order(QTStr("Basic.MainMenu.Edit.Order"), this); - popup.addAction(QTStr("Add"), this, - SLOT(on_actionAddScene_triggered())); + if (!dsk) + popup.addAction(QTStr("Add"), this, + SLOT(on_actionAddScene_triggered())); + else + popup.addAction(QTStr("Add"), this, + SLOT(on_actionAddSceneDSK_triggered())); if (item) { QAction *pasteFilters = @@ -4230,7 +4383,6 @@ void OBSBasic::on_scenes_customContextMenuRequested(const QPoint &pos) pasteFilters->setEnabled(copyFiltersString); connect(pasteFilters, SIGNAL(triggered()), this, SLOT(ScenePasteFilters())); - popup.addSeparator(); popup.addAction(QTStr("Duplicate"), this, SLOT(DuplicateSelectedScene())); @@ -4238,20 +4390,52 @@ void OBSBasic::on_scenes_customContextMenuRequested(const QPoint &pos) SLOT(SceneCopyFilters())); popup.addAction(pasteFilters); popup.addSeparator(); - popup.addAction(QTStr("Rename"), this, SLOT(EditSceneName())); + + if (!dsk) + popup.addAction(QTStr("Rename"), this, + SLOT(EditSceneName())); + else + popup.addAction(QTStr("Rename"), this, + SLOT(EditDSKName())); + popup.addAction(QTStr("Remove"), this, SLOT(RemoveSelectedScene())); popup.addSeparator(); - order.addAction(QTStr("Basic.MainMenu.Edit.Order.MoveUp"), this, + if (!dsk) { + order.addAction( + QTStr("Basic.MainMenu.Edit.Order.MoveUp"), this, SLOT(on_actionSceneUp_triggered())); - order.addAction(QTStr("Basic.MainMenu.Edit.Order.MoveDown"), + order.addAction( + QTStr("Basic.MainMenu.Edit.Order.MoveDown"), this, SLOT(on_actionSceneDown_triggered())); + } else { + order.addAction( + QTStr("Basic.MainMenu.Edit.Order.MoveUp"), this, + SLOT(on_actionSceneUpDSK_triggered())); + order.addAction( + QTStr("Basic.MainMenu.Edit.Order.MoveDown"), + this, SLOT(on_actionSceneDownDSK_triggered())); + } + order.addSeparator(); - order.addAction(QTStr("Basic.MainMenu.Edit.Order.MoveToTop"), + + if (!dsk) { + order.addAction( + QTStr("Basic.MainMenu.Edit.Order.MoveToTop"), this, SLOT(MoveSceneToTop())); - order.addAction(QTStr("Basic.MainMenu.Edit.Order.MoveToBottom"), + order.addAction( + QTStr("Basic.MainMenu.Edit.Order.MoveToBottom"), this, SLOT(MoveSceneToBottom())); + } else { + order.addAction( + QTStr("Basic.MainMenu.Edit.Order.MoveToTop"), + this, SLOT(MoveDSKToTop())); + order.addAction( + QTStr("Basic.MainMenu.Edit.Order.MoveToBottom"), + this, SLOT(MoveDSKToBottom())); + } + popup.addMenu(&order); popup.addSeparator(); @@ -4304,25 +4488,36 @@ void OBSBasic::on_scenes_customContextMenuRequested(const QPoint &pos) popup.addSeparator(); - bool grid = ui->scenes->GetGridMode(); + bool grid = list->GetGridMode(); QAction *gridAction = new QAction(grid ? QTStr("Basic.Main.ListMode") : QTStr("Basic.Main.GridMode"), this); - connect(gridAction, SIGNAL(triggered()), this, - SLOT(on_actionGridMode_triggered())); + + if (!dsk) + connect(gridAction, SIGNAL(triggered()), this, + SLOT(on_actionGridMode_triggered())); + else + connect(gridAction, SIGNAL(triggered()), this, + SLOT(on_actionGridModeDSK_triggered())); + popup.addAction(gridAction); popup.exec(QCursor::pos()); } +void OBSBasic::on_scenes_customContextMenuRequested(const QPoint &pos) +{ + ShowScenesMenu(ui->scenes, pos); +} + void OBSBasic::on_actionGridMode_triggered() { bool gridMode = !ui->scenes->GetGridMode(); ui->scenes->SetGridMode(gridMode); } -void OBSBasic::on_actionAddScene_triggered() +void OBSBasic::AddSceneQuery(bool dsk) { string name; QString format{QTStr("Basic.Main.DefaultSceneName.Text")}; @@ -4358,13 +4553,25 @@ void OBSBasic::on_actionAddScene_triggered() return; } - obs_scene_t *scene = obs_scene_create(name.c_str()); - source = obs_scene_get_source(scene); - SetCurrentScene(source); + obs_scene_t *scene; + + if (dsk) { + scene = obs_dsk_create(name.c_str()); + SetDSKSource(obs_scene_get_source(scene)); + } else { + scene = obs_scene_create(name.c_str()); + SetCurrentScene(obs_scene_get_source(scene)); + } + obs_scene_release(scene); } } +void OBSBasic::on_actionAddScene_triggered() +{ + AddSceneQuery(false); +} + void OBSBasic::on_actionRemoveScene_triggered() { OBSScene scene = GetCurrentScene(); @@ -4374,47 +4581,46 @@ void OBSBasic::on_actionRemoveScene_triggered() obs_source_remove(source); } -void OBSBasic::ChangeSceneIndex(bool relative, int offset, int invalidIdx) +void OBSBasic::ChangeListIndex(QListWidget *list, bool relative, int offset, + int invalidIdx) { - int idx = ui->scenes->currentRow(); + int idx = list->currentRow(); if (idx == -1 || idx == invalidIdx) return; - sceneChanging = true; - - QListWidgetItem *item = ui->scenes->takeItem(idx); + list->blockSignals(true); + QListWidgetItem *item = list->takeItem(idx); if (!relative) idx = 0; - ui->scenes->insertItem(idx + offset, item); - ui->scenes->setCurrentRow(idx + offset); + list->insertItem(idx + offset, item); + list->setCurrentRow(idx + offset); item->setSelected(true); - - sceneChanging = false; + list->blockSignals(false); OBSProjector::UpdateMultiviewProjectors(); } void OBSBasic::on_actionSceneUp_triggered() { - ChangeSceneIndex(true, -1, 0); + ChangeListIndex(ui->scenes, true, -1, 0); } void OBSBasic::on_actionSceneDown_triggered() { - ChangeSceneIndex(true, 1, ui->scenes->count() - 1); + ChangeListIndex(ui->scenes, true, 1, ui->scenes->count() - 1); } void OBSBasic::MoveSceneToTop() { - ChangeSceneIndex(false, 0, 0); + ChangeListIndex(ui->scenes, false, 0, 0); } void OBSBasic::MoveSceneToBottom() { - ChangeSceneIndex(false, ui->scenes->count() - 1, - ui->scenes->count() - 1); + ChangeListIndex(ui->scenes, false, ui->scenes->count() - 1, + ui->scenes->count() - 1); } void OBSBasic::EditSceneItemName() @@ -4883,14 +5089,6 @@ void OBSBasic::AddSourceFromAction() void OBSBasic::AddSourcePopupMenu(const QPoint &pos) { - if (!GetCurrentScene()) { - // Tell the user he needs a scene first (help beginners). - OBSMessageBox::information( - this, QTStr("Basic.Main.AddSourceHelp.Title"), - QTStr("Basic.Main.AddSourceHelp.Text")); - return; - } - QScopedPointer popup(CreateAddSourcePopupMenu()); if (popup) popup->exec(pos); @@ -5156,15 +5354,17 @@ static void RenameListItem(OBSBasic *parent, QListWidget *listWidget, obs_source_release(foundSource); } else { - listItem->setText(QT_UTF8(name.c_str())); - obs_source_set_name(source, name.c_str()); + if (listItem) { + listItem->setText(QT_UTF8(name.c_str())); + obs_source_set_name(source, name.c_str()); + } } } void OBSBasic::SceneNameEdited(QWidget *editor, QAbstractItemDelegate::EndEditHint endHint) { - OBSScene scene = GetCurrentScene(); + OBSScene scene = GetCurrentSceneListScene(); QLineEdit *edit = qobject_cast(editor); string text = QT_TO_UTF8(edit->text().trimmed()); @@ -5180,6 +5380,25 @@ void OBSBasic::SceneNameEdited(QWidget *editor, UNUSED_PARAMETER(endHint); } +void OBSBasic::SceneNameEditedDSK(QWidget *editor, + QAbstractItemDelegate::EndEditHint endHint) +{ + OBSScene scene = GetCurrentGlobalScene(); + QLineEdit *edit = qobject_cast(editor); + string text = QT_TO_UTF8(edit->text().trimmed()); + + if (!scene) + return; + + obs_source_t *source = obs_scene_get_source(scene); + RenameListItem(this, ui->dsk, source, text); + + if (api) + api->on_event(OBS_FRONTEND_EVENT_SCENE_LIST_CHANGED); + + UNUSED_PARAMETER(endHint); +} + void OBSBasic::OpenFilters() { OBSSceneItem item = GetCurrentSceneItem(); @@ -6913,11 +7132,11 @@ void OBSBasic::on_resetUI_triggered() int mixerSize = cx - (cx22_5 * 2 + cx5 * 2); - QList docks{ui->scenesDock, ui->sourcesDock, - ui->mixerDock, ui->transitionsDock, - ui->controlsDock}; + QList docks{ui->scenesDock, ui->sourcesDock, + ui->mixerDock, ui->transitionsDock, + ui->controlsDock, ui->dskDock}; - QList sizes{cx22_5, cx22_5, mixerSize, cx5, cx5}; + QList sizes{cx22_5, cx22_5, mixerSize, cx5, cx5, cx5}; ui->scenesDock->setVisible(true); ui->sourcesDock->setVisible(true); @@ -6926,8 +7145,10 @@ void OBSBasic::on_resetUI_triggered() ui->controlsDock->setVisible(true); statsDock->setVisible(false); statsDock->setFloating(true); + ui->dskDock->setVisible(false); + ui->dskDock->setFloating(true); - resizeDocks(docks, {cy, cy, cy, cy, cy}, Qt::Vertical); + resizeDocks(docks, {cy, cy, cy, cy, cy, cy}, Qt::Vertical); resizeDocks(docks, sizes, Qt::Horizontal); #endif } @@ -6947,6 +7168,7 @@ void OBSBasic::on_lockUI_toggled(bool lock) ui->transitionsDock->setFeatures(mainFeatures); ui->controlsDock->setFeatures(mainFeatures); statsDock->setFeatures(features); + ui->dskDock->setFeatures(features); for (int i = extraDocks.size() - 1; i >= 0; i--) { if (!extraDocks[i]) { @@ -6961,6 +7183,7 @@ void OBSBasic::on_toggleListboxToolbars_toggled(bool visible) { ui->sourcesToolbar->setVisible(visible); ui->scenesToolbar->setVisible(visible); + ui->dskToolbar->setVisible(visible); config_set_bool(App()->GlobalConfig(), "BasicWindow", "ShowListboxToolbars", visible); diff --git a/UI/window-basic-main.hpp b/UI/window-basic-main.hpp index 7d47ce28612cca..8adcf799ead795 100644 --- a/UI/window-basic-main.hpp +++ b/UI/window-basic-main.hpp @@ -161,6 +161,7 @@ class OBSBasic : public OBSMainWindow { friend class ExtraBrowsersModel; friend class ExtraBrowsersDelegate; friend struct OBSStudioAPI; + friend class SourceTree; enum class MoveDir { Up, Down, Left, Right }; @@ -225,7 +226,6 @@ class OBSBasic : public OBSMainWindow { gs_vertbuffer_t *boxBottom = nullptr; gs_vertbuffer_t *circle = nullptr; - bool sceneChanging = false; bool ignoreSelectionUpdate = false; int previewX = 0, previewY = 0; @@ -331,7 +331,11 @@ class OBSBasic : public OBSMainWindow { void LoadSceneListOrder(obs_data_array_t *array); obs_data_array_t *SaveSceneListOrder(); - void ChangeSceneIndex(bool relative, int idx, int invalidIdx); + void ChangeListIndex(QListWidget *list, bool relative, int idx, + int invalidIdx); + + void LoadDSKListOrder(obs_data_array_t *array); + obs_data_array_t *SaveDSKListOrder(); void TempFileOutput(const char *path, int vBitrate, int aBitrate); void TempStreamOutput(const char *url, const char *key, int vBitrate, @@ -354,6 +358,7 @@ class OBSBasic : public OBSMainWindow { void RefreshSceneCollections(); void ChangeSceneCollection(); void LogScenes(); + void LogGlobalScenes(); void LoadProfile(); void ResetProfileData(); @@ -703,10 +708,23 @@ private slots: void DiskSpaceMessage(); OBSSource prevFTBSource = nullptr; + OBSSource prevSceneListSource = nullptr; + + bool IsDSK(OBSScene scene); + void SetDSKSource(OBSSource source); + void AddDSKScene(OBSScene scene); + void AddSceneQuery(bool dsk); + void ShowScenesMenu(SceneTree *list, const QPoint &pos); + void ReorderItemByName(QListWidget *lw, const char *name, int newIndex); + void RemoveSceneListItem(QListWidget *list, obs_scene_t *scene); public: OBSSource GetProgramSource(); OBSScene GetCurrentScene(); + OBSScene GetCurrentGlobalScene(); + OBSScene GetCurrentSceneListScene(); + OBSSource GetCurrentSceneListSource(); + OBSScene GetSceneFromListItem(QListWidgetItem *item); void SysTrayNotify(const QString &text, QSystemTrayIcon::MessageIcon n); @@ -841,15 +859,26 @@ private slots: void on_actionHorizontalCenter_triggered(); void on_customContextMenuRequested(const QPoint &pos); + void on_dsk_customContextMenuRequested(const QPoint &pos); + void on_dsk_currentItemChanged(QListWidgetItem *current, + QListWidgetItem *prev); void on_scenes_currentItemChanged(QListWidgetItem *current, QListWidgetItem *prev); + void on_dsk_itemClicked(QListWidgetItem *item); + void on_scenes_itemClicked(QListWidgetItem *item); + void on_scenes_customContextMenuRequested(const QPoint &pos); void on_actionGridMode_triggered(); + void on_actionGridModeDSK_triggered(); void on_actionAddScene_triggered(); + void on_actionAddSceneDSK_triggered(); void on_actionRemoveScene_triggered(); + void on_actionRemoveSceneDSK_triggered(); void on_actionSceneUp_triggered(); void on_actionSceneDown_triggered(); + void on_actionSceneUpDSK_triggered(); + void on_actionSceneDownDSK_triggered(); void on_sources_customContextMenuRequested(const QPoint &pos); void on_scenes_itemDoubleClicked(QListWidgetItem *item); void on_actionAddSource_triggered(); @@ -931,11 +960,17 @@ private slots: void MoveSceneToTop(); void MoveSceneToBottom(); + void MoveDSKToTop(); + void MoveDSKToBottom(); + void EditSceneName(); + void EditDSKName(); void EditSceneItemName(); void SceneNameEdited(QWidget *editor, QAbstractItemDelegate::EndEditHint endHint); + void SceneNameEditedDSK(QWidget *editor, + QAbstractItemDelegate::EndEditHint endHint); void OpenSceneFilters(); void OpenFilters(); @@ -986,6 +1021,8 @@ public slots: static void InitBrowserPanelSafeBlock(); + bool IsDSKDockVisible(); + private: std::unique_ptr ui; }; diff --git a/UI/window-basic-source-select.cpp b/UI/window-basic-source-select.cpp index 57b7b121fb893f..d99fa1badb51e3 100644 --- a/UI/window-basic-source-select.cpp +++ b/UI/window-basic-source-select.cpp @@ -289,7 +289,7 @@ OBSBasicSourceSelect::OBSBasicSourceSelect(OBSBasic *parent, const char *id_) if (strcmp(id_, "scene") == 0) { OBSBasic *main = reinterpret_cast(App()->GetMainWindow()); - OBSSource curSceneSource = main->GetCurrentSceneSource(); + OBSSource curSceneSource = main->GetCurrentSceneListSource(); ui->selectExisting->setChecked(true); ui->createNew->setChecked(false); diff --git a/UI/window-projector.cpp b/UI/window-projector.cpp index ffb9ea07603e4b..e8da88b9d23328 100644 --- a/UI/window-projector.cpp +++ b/UI/window-projector.cpp @@ -272,7 +272,7 @@ void OBSProjector::OBSRenderMultiview(void *data, uint32_t cx, uint32_t cy) GetScaleAndCenterPos(targetCX, targetCY, cx, cy, x, y, scale); - OBSSource previewSrc = main->GetCurrentSceneSource(); + OBSSource previewSrc = main->GetCurrentSceneListSource(); OBSSource programSrc = main->GetProgramSource(); bool studioMode = main->IsPreviewProgramMode(); @@ -641,7 +641,7 @@ void OBSProjector::OBSRender(void *data, uint32_t cx, uint32_t cy) if (window->type == ProjectorType::Preview && main->IsPreviewProgramMode()) { - OBSSource curSource = main->GetCurrentSceneSource(); + OBSSource curSource = main->GetCurrentSceneListSource(); if (source != curSource) { obs_source_dec_showing(source); @@ -845,7 +845,7 @@ void OBSProjector::mousePressEvent(QMouseEvent *event) return; OBSBasic *main = (OBSBasic *)obs_frontend_get_main_window(); - if (main->GetCurrentSceneSource() != src) + if (main->GetCurrentSceneListSource() != src) main->SetCurrentScene(src, false); } }