From abc26b87fe12f387ea6620d0c41a38bc472e4c98 Mon Sep 17 00:00:00 2001 From: Shaolin Date: Wed, 13 Dec 2017 19:42:19 -0200 Subject: [PATCH] UI: Add Multiview Layout Options --- UI/data/locale/en-US.ini | 5 + UI/forms/OBSBasicSettings.ui | 18 ++- UI/window-basic-settings.cpp | 35 +++++ UI/window-projector.cpp | 239 +++++++++++++++++++++++++++++------ 4 files changed, 255 insertions(+), 42 deletions(-) diff --git a/UI/data/locale/en-US.ini b/UI/data/locale/en-US.ini index 27920d456..796a63133 100644 --- a/UI/data/locale/en-US.ini +++ b/UI/data/locale/en-US.ini @@ -566,6 +566,11 @@ Basic.Settings.General.SystemTrayHideMinimize="Always minimize to system tray in Basic.Settings.General.SaveProjectors="Save projectors on exit" Basic.Settings.General.SwitchOnDoubleClick="Transition to scene when double-clicked" Basic.Settings.General.StudioPortraitLayout="Enable portrait/vertical layout" +Basic.Settings.General.MultiviewLayout="Multiview Layout" +Basic.Settings.General.MultiviewLayout.Horizontal.Top="Horizontal, Top" +Basic.Settings.General.MultiviewLayout.Horizontal.Bottom="Horizontal, Bottom" +Basic.Settings.General.MultiviewLayout.Vertical.Left="Vertical, Left" +Basic.Settings.General.MultiviewLayout.Vertical.Right="Vertical, Right" # basic mode 'stream' settings Basic.Settings.Stream="Stream" diff --git a/UI/forms/OBSBasicSettings.ui b/UI/forms/OBSBasicSettings.ui index 05c48fb34..0384345ba 100644 --- a/UI/forms/OBSBasicSettings.ui +++ b/UI/forms/OBSBasicSettings.ui @@ -146,7 +146,7 @@ 0 0 801 - 715 + 1044 @@ -549,6 +549,9 @@ QFormLayout::AllNonFixedFieldsGrow + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + 2 @@ -579,6 +582,19 @@ + + + + + + + Basic.Settings.General.MultiviewLayout + + + multiviewLayout + + + diff --git a/UI/window-basic-settings.cpp b/UI/window-basic-settings.cpp index 87e885a51..a75607351 100644 --- a/UI/window-basic-settings.cpp +++ b/UI/window-basic-settings.cpp @@ -45,6 +45,7 @@ #include "window-basic-main.hpp" #include "window-basic-settings.hpp" #include "window-basic-main-outputs.hpp" +#include "window-projector.hpp" #include @@ -317,6 +318,7 @@ OBSBasicSettings::OBSBasicSettings(QWidget *parent) HookWidget(ui->snapDistance, DSCROLL_CHANGED,GENERAL_CHANGED); HookWidget(ui->doubleClickSwitch, CHECK_CHANGED, GENERAL_CHANGED); HookWidget(ui->studioPortraitLayout, CHECK_CHANGED, GENERAL_CHANGED); + HookWidget(ui->multiviewLayout, COMBO_CHANGED, GENERAL_CHANGED); HookWidget(ui->outputMode, COMBO_CHANGED, OUTPUTS_CHANGED); HookWidget(ui->streamType, COMBO_CHANGED, STREAM1_CHANGED); HookWidget(ui->simpleOutputPath, EDIT_CHANGED, OUTPUTS_CHANGED); @@ -1087,6 +1089,31 @@ void OBSBasicSettings::LoadGeneralSettings() "BasicWindow", "StudioPortraitLayout"); ui->studioPortraitLayout->setChecked(studioPortraitLayout); + ui->multiviewLayout->addItem(QTStr( + "Basic.Settings.General.MultiviewLayout.Horizontal.Top"), + QT_UTF8("horizontaltop")); + ui->multiviewLayout->addItem(QTStr( + "Basic.Settings.General.MultiviewLayout.Horizontal.Bottom"), + QT_UTF8("horizontalbottom")); + ui->multiviewLayout->addItem(QTStr( + "Basic.Settings.General.MultiviewLayout.Vertical.Left"), + QT_UTF8("verticalleft")); + ui->multiviewLayout->addItem(QTStr( + "Basic.Settings.General.MultiviewLayout.Vertical.Right"), + QT_UTF8("verticalright")); + + const char *multiviewLayoutText = config_get_string(GetGlobalConfig(), + "BasicWindow", "MultiviewLayout"); + + if (astrcmpi(multiviewLayoutText, "horizontalbottom") == 0) + ui->multiviewLayout->setCurrentIndex(1); + else if (astrcmpi(multiviewLayoutText, "verticalleft") == 0) + ui->multiviewLayout->setCurrentIndex(2); + else if (astrcmpi(multiviewLayoutText, "verticalright") == 0) + ui->multiviewLayout->setCurrentIndex(3); + else + ui->multiviewLayout->setCurrentIndex(0); + loading = false; } @@ -2656,6 +2683,14 @@ void OBSBasicSettings::SaveGeneralSettings() main->ResetUI(); } + + if (WidgetChanged(ui->multiviewLayout)) { + config_set_string(GetGlobalConfig(), "BasicWindow", + "MultiviewLayout", + QT_TO_UTF8(GetComboData(ui->multiviewLayout))); + + OBSProjector::UpdateMultiviewProjectors(); + } } void OBSBasicSettings::SaveStream1Settings() diff --git a/UI/window-projector.cpp b/UI/window-projector.cpp index 18b4a66c8..3c14815ca 100644 --- a/UI/window-projector.cpp +++ b/UI/window-projector.cpp @@ -8,8 +8,14 @@ #include "qt-wrappers.hpp" #include "platform.hpp" +#define HORIZONTAL_TOP 0 +#define HORIZONTAL_BOTTOM 1 +#define VERTICAL_LEFT 2 +#define VERTICAL_RIGHT 3 + static QList multiviewProjectors; static bool updatingMultiview = false; +static int multiviewLayout = HORIZONTAL_TOP; OBSProjector::OBSProjector(QWidget *widget, obs_source_t *source_, bool window) : OBSQTDisplay (widget, @@ -245,10 +251,10 @@ void OBSProjector::OBSRenderMultiview(void *data, uint32_t cx, uint32_t cy) OBSBasic *main = (OBSBasic *)obs_frontend_get_main_window(); uint32_t targetCX, targetCY; int x, y; - float fX, fY, halfCX, halfCY, sourceX, sourceY, - quarterCX, quarterCY, scale, targetCXF, targetCYF, - hiCX, hiCY, qiX, qiY, qiCX, qiCY, - hiScaleX, hiScaleY, qiScaleX, qiScaleY; + float fX, fY, halfCX, halfCY, sourceX, sourceY, labelX, labelY, + quarterCX, quarterCY, scale, targetCXF, targetCYF, + hiCX, hiCY, qiX, qiY, qiCX, qiCY, hiScaleX, hiScaleY, + qiScaleX, qiScaleY; uint32_t offset; gs_effect_t *solid = obs_get_base_effect(OBS_EFFECT_SOLID); @@ -317,6 +323,86 @@ void OBSProjector::OBSRenderMultiview(void *data, uint32_t cx, uint32_t cy) gs_projection_pop(); }; + auto calcBaseSource = [&](int i) + { + switch (multiviewLayout) { + case VERTICAL_LEFT: + sourceX = halfCX; + sourceY = (i / 2 ) * quarterCY; + if (i % 2 != 0) + sourceX = halfCX + quarterCX; + break; + case VERTICAL_RIGHT: + sourceX = 0; + sourceY = (i / 2 ) * quarterCY; + if (i % 2 != 0) + sourceX = quarterCX; + break; + case HORIZONTAL_BOTTOM: + if (i < 4) { + sourceX = (float(i) * quarterCX); + sourceY = 0; + } else { + sourceX = (float(i - 4) * quarterCX); + sourceY = quarterCY; + } + break; + default: //HORIZONTAL_TOP: + if (i < 4) { + sourceX = (float(i) * quarterCX); + sourceY = halfCY; + } else { + sourceX = (float(i - 4) * quarterCX); + sourceY = halfCY + quarterCY; + } + } + }; + + auto calcPreviewProgram = [&](bool program) + { + switch (multiviewLayout) { + case VERTICAL_LEFT: + sourceX = 2.0f; + sourceY = halfCY + 2.0f; + labelX = offset; + labelY = halfCY * 1.8f; + if (program) { + sourceY = 2.0f; + labelY = halfCY * 0.8f; + } + break; + case VERTICAL_RIGHT: + sourceX = halfCX + 2.0f; + sourceY = halfCY + 2.0f; + labelX = halfCX + offset; + labelY = halfCY * 1.8f; + if (program) { + sourceY = 2.0f; + labelY = halfCY * 0.8f; + } + break; + case HORIZONTAL_BOTTOM: + sourceX = 2.0f; + sourceY = halfCY + 2.0f; + labelX = offset; + labelY = halfCY * 1.8f; + if (program) { + sourceX = halfCX + 2.0f; + labelX = halfCX + offset; + } + break; + default: //HORIZONTAL_TOP: + sourceX = 2.0f; + sourceY = 2.0f; + labelX = offset; + labelY = halfCY * 0.8f; + if (program) { + sourceX = halfCX + 2.0f; + labelX = halfCX + offset; + } + } + }; + /* ----------------------------- */ /* draw sources */ @@ -334,13 +420,7 @@ void OBSProjector::OBSRenderMultiview(void *data, uint32_t cx, uint32_t cy) if (!label) continue; - if (i < 4) { - sourceX = (float(i) * quarterCX); - sourceY = halfCY; - } else { - sourceX = (float(i - 4) * quarterCX); - sourceY = halfCY + quarterCY; - } + calcBaseSource(i); qiX = sourceX + 4.0f; qiY = sourceY + 4.0f; @@ -399,11 +479,15 @@ void OBSProjector::OBSRenderMultiview(void *data, uint32_t cx, uint32_t cy) /* ----------------------------- */ /* draw preview */ + obs_source_t *previewLabel = window->multiviewLabels[0]; + offset = labelOffset(previewLabel, halfCX); + calcPreviewProgram(false); + gs_matrix_push(); - gs_matrix_translate3f(2.0f, 2.0f, 0.0f); + gs_matrix_translate3f(sourceX, sourceY, 0.0f); gs_matrix_scale3f(hiScaleX, hiScaleY, 1.0f); - setRegion(2.0f, 2.0f, hiCX, hiCY); + setRegion(sourceX, sourceY, hiCX, hiCY); if (studioMode) { obs_source_video_render(previewSrc); @@ -418,7 +502,8 @@ void OBSProjector::OBSRenderMultiview(void *data, uint32_t cx, uint32_t cy) /* ----------- */ gs_matrix_push(); - gs_matrix_scale3f(0.5f, 0.5f, 1.0f); + gs_matrix_translate3f(sourceX, sourceY, 0.0f); + gs_matrix_scale3f(hiScaleX, hiScaleY, 1.0f); renderVB(solid, window->outerBox, targetCX, targetCY); renderVB(solid, window->innerBox, targetCX, targetCY); @@ -432,13 +517,11 @@ void OBSProjector::OBSRenderMultiview(void *data, uint32_t cx, uint32_t cy) /* ----------- */ - obs_source_t *previewLabel = window->multiviewLabels[0]; - offset = labelOffset(previewLabel, halfCX); cx = obs_source_get_width(previewLabel); cy = obs_source_get_height(previewLabel); gs_matrix_push(); - gs_matrix_translate3f(offset, (halfCY * 0.8f), 0.0f); + gs_matrix_translate3f(labelX, labelY, 0.0f); drawBox(cx, cy + int(halfCX * 0.015f), 0xD91F1F1F); obs_source_video_render(previewLabel); @@ -448,11 +531,15 @@ void OBSProjector::OBSRenderMultiview(void *data, uint32_t cx, uint32_t cy) /* ----------------------------- */ /* draw program */ + obs_source_t *programLabel = window->multiviewLabels[1]; + offset = labelOffset(programLabel, halfCX); + calcPreviewProgram(true); + gs_matrix_push(); - gs_matrix_translate3f(halfCX + 2.0, 2.0f, 0.0f); + gs_matrix_translate3f(sourceX, sourceY, 0.0f); gs_matrix_scale3f(hiScaleX, hiScaleY, 1.0f); - setRegion(halfCX + 2.0f, 2.0f, hiCX, hiCY); + setRegion(sourceX, sourceY, hiCX, hiCY); obs_render_main_texture(); resetRegion(); @@ -461,8 +548,8 @@ void OBSProjector::OBSRenderMultiview(void *data, uint32_t cx, uint32_t cy) /* ----------- */ gs_matrix_push(); - gs_matrix_translate3f(halfCX, 0.0f, 0.0f); - gs_matrix_scale3f(0.5f, 0.5f, 1.0f); + gs_matrix_translate3f(sourceX, sourceY, 0.0f); + gs_matrix_scale3f(hiScaleX, hiScaleY, 1.0f); renderVB(solid, window->outerBox, targetCX, targetCY); @@ -470,13 +557,11 @@ void OBSProjector::OBSRenderMultiview(void *data, uint32_t cx, uint32_t cy) /* ----------- */ - obs_source_t *programLabel = window->multiviewLabels[1]; - offset = labelOffset(programLabel, halfCX); cx = obs_source_get_width(programLabel); cy = obs_source_get_height(programLabel); gs_matrix_push(); - gs_matrix_translate3f(halfCX + offset, (halfCY * 0.8f), 0.0f); + gs_matrix_translate3f(labelX, labelY, 0.0f); drawBox(cx, cy + int(halfCX * 0.015f), 0xD91F1F1F); obs_source_video_render(programLabel); @@ -571,26 +656,86 @@ static int getSourceByPosition(int x, int y) int maxY = cy; int halfX = cx / 2; int halfY = cy / 2; + int pos = -1; - if (float(cx) / float(cy) > ratio) { - int validX = cy * ratio; - minX = halfX - (validX / 2); - maxX = halfX + (validX / 2); - } else { - int validY = cx / ratio; - maxY = halfY + (validY / 2); + switch (multiviewLayout) { + case VERTICAL_LEFT: + if (float(cx) / float(cy) > ratio) { + int validX = cy * ratio; + maxX = halfX + (validX / 2); + } else { + int validY = cx / ratio; + minY = halfY - (validY / 2); + maxY = halfY + (validY / 2); + } + + minX = halfX; + + if (x < minX || x > maxX || y < minY || y > maxY) + break; + + pos = 2 * ((y - minY) / ((maxY - minY) / 4)); + if (x > minX + ((maxX - minX) / 2)) + pos++; + break; + case VERTICAL_RIGHT: + if (float(cx) / float(cy) > ratio) { + int validX = cy * ratio; + minX = halfX - (validX / 2); + } else { + int validY = cx / ratio; + minY = halfY - (validY / 2); + maxY = halfY + (validY / 2); + } + + maxX = halfX; + + if (x < minX || x > maxX || y < minY || y > maxY) + break; + + pos = 2 * ((y - minY) / ((maxY - minY) / 4)); + if (x > minX + ((maxX - minX) / 2)) + pos++; + break; + case HORIZONTAL_BOTTOM: + if (float(cx) / float(cy) > ratio) { + int validX = cy * ratio; + minX = halfX - (validX / 2); + maxX = halfX + (validX / 2); + } else { + int validY = cx / ratio; + minY = halfY - (validY / 2); + } + + maxY = halfY; + + if (x < minX || x > maxX || y < minY || y > maxY) + break; + + pos = (x - minX) / ((maxX - minX) / 4); + if (y > minY + ((maxY - minY) / 2)) + pos += 4; + break; + default: // HORIZONTAL_TOP + if (float(cx) / float(cy) > ratio) { + int validX = cy * ratio; + minX = halfX - (validX / 2); + maxX = halfX + (validX / 2); + } else { + int validY = cx / ratio; + maxY = halfY + (validY / 2); + } + + minY = halfY; + + if (x < minX || x > maxX || y < minY || y > maxY) + break; + + pos = (x - minX) / ((maxX - minX) / 4); + if (y > minY + ((maxY - minY) / 2)) + pos += 4; } - minY = halfY; - - if (x < minX || x > maxX || y < minY || y > maxY) - return -1; - - int quarterX = (maxX - minX) / 4; - int pos = (x - minX) / quarterX; - if (y > minY + ((maxY - minY) / 2)) - pos += 4; - return pos; } @@ -699,6 +844,18 @@ void OBSProjector::UpdateMultiview() } obs_frontend_source_list_free(&scenes); + + const char *multiviewLayoutText = config_get_string(GetGlobalConfig(), + "BasicWindow", "MultiviewLayout"); + + if (astrcmpi(multiviewLayoutText, "horizontalbottom") == 0) + multiviewLayout = HORIZONTAL_BOTTOM; + else if (astrcmpi(multiviewLayoutText, "verticalleft") == 0) + multiviewLayout = VERTICAL_LEFT; + else if (astrcmpi(multiviewLayoutText, "verticalright") == 0) + multiviewLayout = VERTICAL_RIGHT; + else + multiviewLayout = HORIZONTAL_TOP; } void OBSProjector::UpdateMultiviewProjectors()