diff --git a/UI/CMakeLists.txt b/UI/CMakeLists.txt index df4d264500419c..90c5a054490a4e 100644 --- a/UI/CMakeLists.txt +++ b/UI/CMakeLists.txt @@ -252,7 +252,8 @@ set(obs_SOURCES obs-proxy-style.cpp locked-checkbox.cpp visibility-checkbox.cpp - media-slider.cpp) + media-slider.cpp + scene-wizard.cpp) set(obs_HEADERS ${obs_PLATFORM_HEADERS} @@ -322,7 +323,8 @@ set(obs_HEADERS log-viewer.hpp obs-proxy-style.hpp obs-proxy-style.hpp - media-slider.hpp) + media-slider.hpp + scene-wizard.hpp) set(obs_importers_HEADERS importers/importers.hpp) @@ -362,7 +364,8 @@ set(obs_UI forms/OBSUpdate.ui forms/OBSRemux.ui forms/OBSImporter.ui - forms/OBSAbout.ui) + forms/OBSAbout.ui + forms/OBSSceneWizard.ui) set(obs_QRC forms/obs.qrc) diff --git a/UI/data/locale/en-US.ini b/UI/data/locale/en-US.ini index 5d3870adbe6b54..4bf75694afcd05 100644 --- a/UI/data/locale/en-US.ini +++ b/UI/data/locale/en-US.ini @@ -1053,3 +1053,16 @@ ContextBar.MediaControls.PlaylistNext="Next in Playlist" ContextBar.MediaControls.PlaylistPrevious="Previous in Playlist" ContextBar.MediaControls.MediaProperties="Media Properties" ContextBar.MediaControls.BlindSeek="Media Seek Widget" + +# SceneWizard +SceneWizard="Scene Wizard" +RunSceneWizard="Run Scene Wizard" +SceneWizard.OptionsQuestion="How do you want to use OBS?" +SceneWizard.ImportButton="I want to import my existing scenes" +SceneWizard.TemplateButton="I want to choose a scene template" +SceneWizard.SetupOwnButton="I will setup OBS on my own" +SceneWizard.SelectTemplate="Select template" +SceneWizard.NoTemplates="No templates available" +SceneWizard.SelectWebcam="Select webcam" +SceneWizard.SelectMicrophone="Select microphone" +SceneWizard.SelectDesktopAudio="Select desktop audio" diff --git a/UI/data/templates/mac/.keep b/UI/data/templates/mac/.keep new file mode 100644 index 00000000000000..e69de29bb2d1d6 diff --git a/UI/data/templates/nix/.keep b/UI/data/templates/nix/.keep new file mode 100644 index 00000000000000..e69de29bb2d1d6 diff --git a/UI/data/templates/windows/.keep b/UI/data/templates/windows/.keep new file mode 100644 index 00000000000000..e69de29bb2d1d6 diff --git a/UI/data/themes/Acri.qss b/UI/data/themes/Acri.qss index 8a6fdf7bb574e0..a064c75b933b7d 100644 --- a/UI/data/themes/Acri.qss +++ b/UI/data/themes/Acri.qss @@ -1152,3 +1152,20 @@ QSlider::handle:horizontal[themeID="tBarSlider"] { * [themeID="previousIcon"] { qproperty-icon: url(./Dark/media/media_previous.svg); } + +/* Scene wizard */ + +QListWidget[themeID="templateList"] { + background-color: #181819; + border: none; +} + +QListWidget::item:selected[themeID="templateList"] { + background-color: #162458; + color: rgb(225, 224, 225); +} + +QListWidget::item[themeID="templateList"] { + border: 0px; + padding: 10px; +} diff --git a/UI/data/themes/Dark.qss b/UI/data/themes/Dark.qss index b9ab60a5a8bc71..eb0f882ace8946 100644 --- a/UI/data/themes/Dark.qss +++ b/UI/data/themes/Dark.qss @@ -878,3 +878,20 @@ QSlider::handle:horizontal[themeID="tBarSlider"] { * [themeID="previousIcon"] { qproperty-icon: url(./Dark/media/media_previous.svg); } + +/* Scene wizard */ + +QListWidget[themeID="templateList"] { + background-color: rgb(58, 57, 58); + border: none; +} + +QListWidget::item:selected[themeID="templateList"] { + background-color: rgb(88, 87, 88); + color: rgb(225, 224, 225); +} + +QListWidget::item[themeID="templateList"] { + border: 0px; + padding: 10px; +} diff --git a/UI/data/themes/Rachni.qss b/UI/data/themes/Rachni.qss index 5ae927c40f7d67..f6d1cea61241f3 100644 --- a/UI/data/themes/Rachni.qss +++ b/UI/data/themes/Rachni.qss @@ -1459,3 +1459,20 @@ QPushButton#sourceFiltersButton { * [themeID="nextIcon"] { qproperty-icon: url(./Dark/media/media_next.svg); } + +/* Scene wizard */ + +QListWidget[themeID="templateList"] { + background-color: rgb(49, 54, 59); /* Blue-gray */ + border: none; +} + +QListWidget::item:selected[themeID="templateList"] { + background-color: rgb(0, 188, 212);; /* Cyan (Primary) */ + color: rgb(225, 224, 225); +} + +QListWidget::item[themeID="templateList"] { + border: 0px; + padding: 10px; +} diff --git a/UI/data/themes/System.qss b/UI/data/themes/System.qss index 125c5089eda30b..bb530029d8bfb5 100644 --- a/UI/data/themes/System.qss +++ b/UI/data/themes/System.qss @@ -296,3 +296,10 @@ QSlider::handle:horizontal[themeID="tBarSlider"] { * [themeID="previousIcon"] { qproperty-icon: url(./Dark/media/media_previous.svg); } + +/* Scene wizard */ + +QListWidget::item[themeID="templateList"] { + border: 0px; + padding: 10px; +} diff --git a/UI/forms/OBSBasic.ui b/UI/forms/OBSBasic.ui index 8cc20521926764..00929623c6bb72 100644 --- a/UI/forms/OBSBasic.ui +++ b/UI/forms/OBSBasic.ui @@ -426,7 +426,7 @@ 0 0 1079 - 21 + 22 @@ -587,6 +587,8 @@ Basic.MainMenu.SceneCollection + + @@ -649,7 +651,7 @@ - QDockWidget::DockWidgetClosable|QDockWidget::DockWidgetMovable|QDockWidget::DockWidgetFloatable + QDockWidget::AllDockWidgetFeatures Basic.Main.Scenes @@ -784,7 +786,7 @@ - QDockWidget::DockWidgetClosable|QDockWidget::DockWidgetMovable|QDockWidget::DockWidgetFloatable + QDockWidget::AllDockWidgetFeatures Basic.Main.Sources @@ -920,7 +922,7 @@ - QDockWidget::DockWidgetClosable|QDockWidget::DockWidgetMovable|QDockWidget::DockWidgetFloatable + QDockWidget::AllDockWidgetFeatures Mixer @@ -971,7 +973,7 @@ 0 0 - 94 + 74 16 @@ -1060,7 +1062,7 @@ - QDockWidget::DockWidgetClosable|QDockWidget::DockWidgetMovable|QDockWidget::DockWidgetFloatable + QDockWidget::AllDockWidgetFeatures Basic.SceneTransitions @@ -1221,7 +1223,7 @@ - QDockWidget::DockWidgetClosable|QDockWidget::DockWidgetMovable|QDockWidget::DockWidgetFloatable + QDockWidget::AllDockWidgetFeatures Basic.Main.Controls @@ -2005,6 +2007,11 @@ Basic.MainMenu.View.ContextBar + + + RunSceneWizard + + diff --git a/UI/forms/OBSSceneWizard.ui b/UI/forms/OBSSceneWizard.ui new file mode 100644 index 00000000000000..d1c7942c4da1bf --- /dev/null +++ b/UI/forms/OBSSceneWizard.ui @@ -0,0 +1,571 @@ + + + SceneWizard + + + + 0 + 0 + 709 + 503 + + + + SceneWizard + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + 0 + 60 + + + + + 16777215 + 60 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 20 + + + 20 + + + + + + 75 + true + + + + Back + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 75 + true + + + + Next + + + + + + + + + + + + 1 + + + + + 20 + + + 10 + + + 20 + + + 10 + + + 0 + + + 6 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + 12 + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + 75 + true + + + + SceneWizard.OptionsQuestion + + + + + + + + 400 + 32 + + + + SceneWizard.TemplateButton + + + + + + + + 250 + 32 + + + + SceneWizard.ImportButton + + + + + + + + 400 + 32 + + + + SceneWizard.SetupOwnButton + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + 20 + + + 10 + + + 20 + + + 10 + + + 0 + + + + + + + + 0 + 0 + + + + + 75 + true + + + + SceneWizard.SelectTemplate + + + + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 20 + 10 + + + + + + + + SceneWizard.NoTemplates + + + + + + + + 200 + 200 + + + + QListView::Static + + + QListView::Adjust + + + 6 + + + + 220 + 220 + + + + QListView::IconMode + + + true + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + + + + + + 20 + + + 10 + + + 20 + + + 10 + + + 0 + + + 6 + + + + + 6 + + + + + 0 + + + + + + 75 + true + + + + SceneWizard.SelectWebcam + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Disable + + + + + + + + + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 20 + 10 + + + + + + + + + + + 75 + true + + + + SceneWizard.SelectMicrophone + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Disable + + + + + + + + + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 20 + 10 + + + + + + + + + + + 75 + true + + + + SceneWizard.SelectDesktopAudio + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Disable + + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + + + + ClickableLabel + QLabel +
clickable-label.hpp
+
+
+ + +
diff --git a/UI/forms/images/no-image.png b/UI/forms/images/no-image.png new file mode 100644 index 00000000000000..afe66312346b19 Binary files /dev/null and b/UI/forms/images/no-image.png differ diff --git a/UI/forms/obs.qrc b/UI/forms/obs.qrc index 97118eba7d7ead..f9895f9a630f14 100644 --- a/UI/forms/obs.qrc +++ b/UI/forms/obs.qrc @@ -47,6 +47,7 @@ images/media/media_previous.svg images/media/media_restart.svg images/media/media_stop.svg + images/no-image.png images/settings/output.svg diff --git a/UI/obs-app.cpp b/UI/obs-app.cpp index 136761e3b5b11b..ec285eb7dcf45a 100644 --- a/UI/obs-app.cpp +++ b/UI/obs-app.cpp @@ -1460,12 +1460,15 @@ bool OBSApp::IsUpdaterDisabled() #ifdef __APPLE__ #define INPUT_AUDIO_SOURCE "coreaudio_input_capture" #define OUTPUT_AUDIO_SOURCE "coreaudio_output_capture" +#define VIDEO_CAPTURE_SOURCE "av_capture_input" #elif _WIN32 #define INPUT_AUDIO_SOURCE "wasapi_input_capture" #define OUTPUT_AUDIO_SOURCE "wasapi_output_capture" +#define VIDEO_CAPTURE_SOURCE "dshow_input" #else #define INPUT_AUDIO_SOURCE "pulse_input_capture" #define OUTPUT_AUDIO_SOURCE "pulse_output_capture" +#define VIDEO_CAPTURE_SOURCE "v4l2_input" #endif const char *OBSApp::InputAudioSource() const @@ -1478,6 +1481,11 @@ const char *OBSApp::OutputAudioSource() const return OUTPUT_AUDIO_SOURCE; } +const char *OBSApp::VideoCaptureSource() const +{ + return VIDEO_CAPTURE_SOURCE; +} + const char *OBSApp::GetLastLog() const { return lastLogFile.c_str(); diff --git a/UI/obs-app.hpp b/UI/obs-app.hpp index cb67df0e4f8738..909ff6618b9033 100644 --- a/UI/obs-app.hpp +++ b/UI/obs-app.hpp @@ -153,6 +153,7 @@ class OBSApp : public QApplication { const char *InputAudioSource() const; const char *OutputAudioSource() const; + const char *VideoCaptureSource() const; const char *GetRenderModule() const; diff --git a/UI/scene-wizard.cpp b/UI/scene-wizard.cpp new file mode 100644 index 00000000000000..4d95dd6546cb83 --- /dev/null +++ b/UI/scene-wizard.cpp @@ -0,0 +1,444 @@ +#include "scene-wizard.hpp" +#include "obs-app.hpp" +#include "platform.hpp" +#include "qt-wrappers.hpp" +#include "display-helpers.hpp" + +#include + +static void SaveSourceSettings(OBSSource source, const char *id) +{ + if (!source) + return; + + OBSData settings = obs_data_create(); + obs_data_release(settings); + + obs_data_set_string(settings, "device_id", id); + obs_source_update(source, settings); +} + +void SceneWizard::SaveMicrophone() +{ + if (ui->disableMic->isChecked()) { + obs_set_output_source(3, nullptr); + return; + } + + OBSSource source = obs_get_output_source(3); + obs_source_release(source); + + if (!source) + return; + + int index = ui->micCombo->currentIndex(); + QString id = ui->micCombo->itemData(index).toString(); + SaveSourceSettings(source, QT_TO_UTF8(id)); +} + +void SceneWizard::SaveDesktopAudio() +{ + if (ui->disableDesktopAudio->isChecked()) { + obs_set_output_source(1, nullptr); + return; + } + + OBSSource source = obs_get_output_source(1); + obs_source_release(source); + + if (!source) + return; + + int index = ui->desktopAudioCombo->currentIndex(); + QString id = ui->desktopAudioCombo->itemData(index).toString(); + SaveSourceSettings(source, QT_TO_UTF8(id)); +} + +void SceneWizard::RemoveWebcam() +{ + if (ui->disableWebcam->isChecked()) + return; + + auto cb = [](void *, obs_source_t *source) { + if (!source) + return true; + + const char *id = obs_source_get_id(source); + + if (strcmp(id, App()->VideoCaptureSource()) == 0) + obs_source_remove(source); + + return true; + }; + + obs_enum_sources(cb, nullptr); +} + +void SceneWizard::SaveWebcam() +{ + if (ui->disableWebcam->isChecked()) { + RemoveWebcam(); + return; + } + + int index = ui->webcamCombo->currentIndex(); + + if (index == 0) + return; + + const char *id = + QT_TO_UTF8(ui->webcamCombo->itemData(index).toString()); + + auto cb = [](void *data, obs_source_t *source) { + if (!source) + return true; + + const char *id = obs_source_get_id(source); + const char *deviceID = reinterpret_cast(data); + + if (strcmp(id, App()->VideoCaptureSource()) == 0) + SaveSourceSettings(source, deviceID); + + return true; + }; + + obs_enum_sources(cb, &id); +} + +void SceneWizard::on_micCombo_currentIndexChanged(int index) +{ + QString id = ui->desktopAudioCombo->itemData(index).toString(); + SaveSourceSettings(mic, QT_TO_UTF8(id)); +} + +void SceneWizard::on_desktopAudioCombo_currentIndexChanged(int index) +{ + QString id = ui->desktopAudioCombo->itemData(index).toString(); + SaveSourceSettings(desktopAudio, QT_TO_UTF8(id)); +} + +void SceneWizard::on_webcamCombo_currentIndexChanged(int index) +{ + QString id = ui->webcamCombo->itemData(index).toString(); + SaveSourceSettings(cam, QT_TO_UTF8(id)); +} + +static inline void LoadListValue(QComboBox *widget, const char *text, + const char *val) +{ + widget->addItem(QT_UTF8(text), QT_UTF8(val)); +} + +static void LoadListValues(QComboBox *widget, obs_property_t *prop) +{ + size_t count = obs_property_list_item_count(prop); + + for (size_t i = 0; i < count; i++) { + const char *name = obs_property_list_item_name(prop, i); + const char *val = obs_property_list_item_string(prop, i); + LoadListValue(widget, name, val); + } +} + +void SceneWizard::LoadDevices() +{ + obs_properties_t *cam_props = obs_source_properties(cam); + obs_properties_t *input_props = obs_source_properties(mic); + obs_properties_t *output_props = obs_source_properties(desktopAudio); + + if (cam_props) { + obs_property_t *cams = + obs_properties_get(cam_props, "device_id"); + LoadListValues(ui->webcamCombo, cams); + obs_properties_destroy(cam_props); + } + + if (input_props) { + obs_property_t *inputs = + obs_properties_get(input_props, "device_id"); + LoadListValues(ui->micCombo, inputs); + obs_properties_destroy(input_props); + } + + if (output_props) { + obs_property_t *outputs = + obs_properties_get(output_props, "device_id"); + LoadListValues(ui->desktopAudioCombo, outputs); + obs_properties_destroy(output_props); + } +} + +void SceneWizard::OBSRender(void *data, uint32_t cx, uint32_t cy) +{ + SceneWizard *window = static_cast(data); + + if (!window->cam) + return; + + uint32_t sourceCX = std::max(obs_source_get_width(window->cam), 1u); + uint32_t sourceCY = std::max(obs_source_get_height(window->cam), 1u); + + int x, y; + int newCX, newCY; + float scale; + + GetScaleAndCenterPos(sourceCX, sourceCY, cx, cy, x, y, scale); + + newCX = int(scale * float(sourceCX)); + newCY = int(scale * float(sourceCY)); + + gs_viewport_push(); + gs_projection_push(); + gs_ortho(0.0f, float(sourceCX), 0.0f, float(sourceCY), -100.0f, 100.0f); + gs_set_viewport(x, y, newCX, newCY); + + obs_source_video_render(window->cam); + + gs_projection_pop(); + gs_viewport_pop(); +} + +SceneWizard::SceneWizard(QWidget *parent) + : QDialog(parent), ui(new Ui::SceneWizard) +{ + ui->setupUi(this); + setFixedSize(750, 750); + + ui->back->setText("" + + QTStr("Back") + ""); + ui->next->setText("" + + QTStr("Next") + ""); + ui->bottomWidget->setProperty("themeID", "aboutHLayout"); + ui->back->setProperty("themeID", "aboutHLayout"); + ui->next->setProperty("themeID", "aboutHLayout"); + ui->templateList->setProperty("themeID", "templateList"); + + style()->unpolish(ui->templateList); + style()->polish(ui->templateList); + + ui->noTemplates->hide(); + + InitTemplates(); + + mic = obs_source_create(App()->InputAudioSource(), "microphone", + nullptr, nullptr); + desktopAudio = obs_source_create(App()->OutputAudioSource(), + "desktop_audio", nullptr, nullptr); + cam = obs_source_create_private(App()->VideoCaptureSource(), nullptr, + nullptr); + + obs_source_release(mic); + obs_source_release(desktopAudio); + obs_source_release(cam); + + preview = new OBSQTDisplay(ui->devicePage); + preview->setMinimumSize(20, 150); + preview->setSizePolicy( + QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding)); + + auto addDrawCallback = [this]() { + obs_display_add_draw_callback(preview->GetDisplay(), + SceneWizard::OBSRender, this); + }; + + preview->show(); + connect(preview.data(), &OBSQTDisplay::DisplayCreated, addDrawCallback); + + LoadDevices(); + + micVol = new VolControl(mic, true, false); + desktopVol = new VolControl(desktopAudio, true, false); + + micVol->slider->hide(); + micVol->config->hide(); + micVol->nameLabel->hide(); + micVol->volLabel->hide(); + micVol->mute->hide(); + + desktopVol->slider->hide(); + desktopVol->config->hide(); + desktopVol->nameLabel->hide(); + desktopVol->volLabel->hide(); + desktopVol->mute->hide(); + + ui->deviceLayout->insertWidget(2, preview); + ui->deviceLayout->insertWidget(6, micVol); + ui->deviceLayout->insertWidget(10, desktopVol); + + ui->templateList->setCurrentRow(0); + GoToOptionsPage(); +} + +SceneWizard::~SceneWizard() +{ + obs_display_remove_draw_callback(preview->GetDisplay(), + SceneWizard::OBSRender, this); +} + +void SceneWizard::UpdateSceneCollection() +{ + OBSBasic *main = reinterpret_cast(App()->GetMainWindow()); + QString templatePath; + + QListWidgetItem *item = ui->templateList->currentItem(); + + if (item) + templatePath = item->data(Qt::UserRole).toString(); + + if (!templatePath.isEmpty()) { + main->SaveProjectNow(); + main->Load(QT_TO_UTF8(templatePath)); + main->RefreshSceneCollections(); + + const char *newName = config_get_string( + App()->GlobalConfig(), "Basic", "SceneCollection"); + const char *newFile = config_get_string( + App()->GlobalConfig(), "Basic", "SceneCollectionFile"); + + blog(LOG_INFO, "Switched to scene collection '%s' (%s.json)", + newName, newFile); + blog(LOG_INFO, + "------------------------------------------------"); + + main->UpdateTitleBar(); + + if (main->api) + main->api->on_event( + OBS_FRONTEND_EVENT_SCENE_COLLECTION_CHANGED); + } + + SaveMicrophone(); + SaveDesktopAudio(); + SaveWebcam(); + + close(); +} + +void SceneWizard::on_importButton_clicked() +{ + close(); + + OBSBasic *main = reinterpret_cast(App()->GetMainWindow()); + main->on_actionImportSceneCollection_triggered(); +} + +void SceneWizard::on_templateButton_clicked() +{ + GoToTemplatePage(); +} + +void SceneWizard::on_setupOnOwnButton_clicked() +{ + close(); +} + +void SceneWizard::on_disableWebcam_toggled(bool checked) +{ + ui->webcamCombo->setEnabled(!checked); +} + +void SceneWizard::on_disableMic_toggled(bool checked) +{ + ui->micCombo->setEnabled(!checked); +} + +void SceneWizard::on_disableDesktopAudio_toggled(bool checked) +{ + ui->desktopAudioCombo->setEnabled(!checked); +} + +void SceneWizard::GoToOptionsPage() +{ + ui->stackedWidget->setCurrentWidget(ui->optionPage); + ui->bottomWidget->hide(); +} + +void SceneWizard::GoToTemplatePage() +{ + ui->stackedWidget->setCurrentWidget(ui->templatePage); + ui->bottomWidget->show(); +} + +void SceneWizard::GoToDevicesPage() +{ + ui->stackedWidget->setCurrentWidget(ui->devicePage); + ui->bottomWidget->show(); + ui->next->setText("" + + QTStr("Finish") + ""); +} + +void SceneWizard::on_back_clicked() +{ + QWidget *widget = ui->stackedWidget->currentWidget(); + + if (widget == ui->devicePage) + GoToTemplatePage(); + else if (widget == ui->templatePage) + GoToOptionsPage(); +} + +void SceneWizard::on_next_clicked() +{ + QWidget *widget = ui->stackedWidget->currentWidget(); + + if (widget == ui->devicePage) { + UpdateSceneCollection(); + close(); + } else if (widget == ui->templatePage) { + GoToDevicesPage(); + } +} + +void SceneWizard::closeEvent(QCloseEvent *event) +{ + deleteLater(); + QDialog::closeEvent(event); +} + +#ifdef __APPLE__ +#define TEMPLATE_FOLDER "mac" +#elif _WIN32 +#define TEMPLATE_FOLDER "windows" +#else +#define TEMPLATE_FOLDER "nix" +#endif + +void SceneWizard::InitTemplates() +{ + std::string path; + + if (!GetDataFilePath("templates/", path)) + return; + + QDir folder(QT_UTF8(path.c_str()) + TEMPLATE_FOLDER + "/"); + + QStringList files = + folder.entryList(QStringList() << "*.json", QDir::Files); + + if (files.count() == 0) { + ui->noTemplates->show(); + return; + } + + for (auto file : files) { + QString filename; + filename += QT_UTF8(path.c_str()) + TEMPLATE_FOLDER + "/"; + filename += file; + + QListWidgetItem *item = new QListWidgetItem(); + item->setData(Qt::UserRole, filename); + item->setText(file.replace(".json", "")); + + QPixmap pixmap(filename.replace(".json", ".png")); + + if (!pixmap) + pixmap.load(QString(":res/images/no-image.png")); + + QIcon icon; + icon.addPixmap(pixmap, QIcon::Normal); + icon.addPixmap(pixmap, QIcon::Selected); + item->setIcon(icon); + + ui->templateList->addItem(item); + } +} diff --git a/UI/scene-wizard.hpp b/UI/scene-wizard.hpp new file mode 100644 index 00000000000000..3dce33efd98f58 --- /dev/null +++ b/UI/scene-wizard.hpp @@ -0,0 +1,59 @@ +#pragma once + +#include +#include +#include "ui_OBSSceneWizard.h" +#include "window-basic-main.hpp" +#include "qt-display.hpp" +#include "volume-control.hpp" +#include "mute-checkbox.hpp" + +class SceneWizard : public QDialog { + Q_OBJECT + +public: + SceneWizard(QWidget *parent = nullptr); + ~SceneWizard(); + + OBSSource webcam = nullptr; + OBSSource mic = nullptr; + OBSSource desktopAudio = nullptr; + OBSSource cam = nullptr; + QPointer preview; + +private: + std::unique_ptr ui; + void InitTemplates(); + void UpdateSceneCollection(); + void LoadDevices(); + void SaveMicrophone(); + void SaveDesktopAudio(); + void SaveWebcam(); + void RemoveWebcam(); + + QPointer micVol; + QPointer desktopVol; + + static void OBSRender(void *data, uint32_t cx, uint32_t cy); + +private slots: + void on_templateButton_clicked(); + void on_importButton_clicked(); + void on_setupOnOwnButton_clicked(); + + void on_back_clicked(); + void on_next_clicked(); + void on_desktopAudioCombo_currentIndexChanged(int index); + void on_micCombo_currentIndexChanged(int index); + void on_webcamCombo_currentIndexChanged(int index); + void on_disableWebcam_toggled(bool checked); + void on_disableMic_toggled(bool checked); + void on_disableDesktopAudio_toggled(bool checked); + + void GoToOptionsPage(); + void GoToTemplatePage(); + void GoToDevicesPage(); + +protected: + virtual void closeEvent(QCloseEvent *event); +}; diff --git a/UI/volume-control.hpp b/UI/volume-control.hpp index 82a2bcb50b99d6..01cdc38bbe3eed 100644 --- a/UI/volume-control.hpp +++ b/UI/volume-control.hpp @@ -7,6 +7,7 @@ #include #include #include +#include "scene-wizard.hpp" class QPushButton; class VolumeMeterTimer; @@ -206,6 +207,8 @@ class MuteCheckBox; class VolControl : public QWidget { Q_OBJECT + friend class SceneWizard; + private: OBSSource source; QLabel *nameLabel; diff --git a/UI/window-basic-main.cpp b/UI/window-basic-main.cpp index 4b5db15f43eea9..df70f2792b77d9 100644 --- a/UI/window-basic-main.cpp +++ b/UI/window-basic-main.cpp @@ -58,6 +58,7 @@ #include "remote-text.hpp" #include "ui-validation.hpp" #include "media-controls.hpp" +#include "scene-wizard.hpp" #include #include @@ -7932,6 +7933,9 @@ void OBSBasic::on_autoConfigure_triggered() test.setModal(true); test.show(); test.exec(); + + QMetaObject::invokeMethod(this, "on_actionRunSceneWizard_triggered", + Qt::QueuedConnection); } void OBSBasic::on_stats_triggered() @@ -8367,3 +8371,9 @@ void OBSBasic::on_sourceFiltersButton_clicked() { OpenFilters(); } + +void OBSBasic::on_actionRunSceneWizard_triggered() +{ + SceneWizard sw(this); + sw.exec(); +} diff --git a/UI/window-basic-main.hpp b/UI/window-basic-main.hpp index 8e960c675fdaa3..9b761ceb386239 100644 --- a/UI/window-basic-main.hpp +++ b/UI/window-basic-main.hpp @@ -35,6 +35,7 @@ #include "window-basic-about.hpp" #include "auth-base.hpp" #include "log-viewer.hpp" +#include "scene-wizard.hpp" #include @@ -167,6 +168,7 @@ class OBSBasic : public OBSMainWindow { friend class DeviceToolbarPropertiesThread; friend struct BasicOutputHandler; friend struct OBSStudioAPI; + friend class SceneWizard; enum class MoveDir { Up, Down, Left, Right }; @@ -870,6 +872,8 @@ private slots: void on_actionVerticalCenter_triggered(); void on_actionHorizontalCenter_triggered(); + void on_actionRunSceneWizard_triggered(); + void on_customContextMenuRequested(const QPoint &pos); void on_scenes_currentItemChanged(QListWidgetItem *current,