2013-11-07 16:45:03 -07:00
|
|
|
/******************************************************************************
|
2023-05-18 17:37:26 -07:00
|
|
|
Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>
|
|
|
|
|
Zachary Lund <admin@computerquip.com>
|
|
|
|
|
Philippe Groarke <philippe.groarke@gmail.com>
|
2013-11-07 16:45:03 -07:00
|
|
|
|
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
|
|
|
it under the terms of the GNU General Public License as published by
|
2013-12-02 22:24:38 -07:00
|
|
|
the Free Software Foundation, either version 2 of the License, or
|
2013-11-07 16:45:03 -07:00
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful,
|
2013-11-09 00:19:38 -07:00
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
2013-11-07 16:45:03 -07:00
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
******************************************************************************/
|
2017-02-20 04:46:29 -08:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
#include "OBSBasic.hpp"
|
|
|
|
|
#include "ColorSelect.hpp"
|
|
|
|
|
#include "OBSProjector.hpp"
|
|
|
|
|
#include "VolControl.hpp"
|
2022-07-31 18:04:55 +02:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
#include <dialogs/NameDialog.hpp>
|
|
|
|
|
#include <dialogs/OBSBasicAdvAudio.hpp>
|
|
|
|
|
#include <dialogs/OBSBasicSourceSelect.hpp>
|
|
|
|
|
#include <utility/item-widget-helpers.hpp>
|
2022-11-05 22:32:43 +01:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
#include <qt-wrappers.hpp>
|
2014-01-09 19:51:51 -06:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
#include <QWidgetAction>
|
2014-04-16 22:35:01 +02:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
#include <sstream>
|
2020-08-19 15:09:26 -03:00
|
|
|
|
2013-12-29 08:54:06 -07:00
|
|
|
using namespace std;
|
2013-11-22 16:20:52 -07:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
static inline bool HasAudioDevices(const char *source_id)
|
|
|
|
|
{
|
|
|
|
|
const char *output_id = source_id;
|
|
|
|
|
obs_properties_t *props = obs_get_source_properties(output_id);
|
|
|
|
|
size_t count = 0;
|
2019-03-26 22:01:22 -07:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
if (!props)
|
|
|
|
|
return false;
|
2014-02-02 14:26:23 -07:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
obs_property_t *devices = obs_properties_get(props, "device_id");
|
|
|
|
|
if (devices)
|
|
|
|
|
count = obs_property_list_item_count(devices);
|
2019-08-27 20:08:43 +12:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
obs_properties_destroy(props);
|
2019-08-27 20:08:43 +12:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
return count != 0;
|
2015-06-28 03:21:45 +02:00
|
|
|
}
|
|
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
void OBSBasic::CreateFirstRunSources()
|
2015-06-28 03:21:45 +02:00
|
|
|
{
|
2024-12-11 17:43:35 +01:00
|
|
|
bool hasDesktopAudio = HasAudioDevices(App()->OutputAudioSource());
|
|
|
|
|
bool hasInputAudio = HasAudioDevices(App()->InputAudioSource());
|
2015-06-28 03:21:45 +02:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
#ifdef __APPLE__
|
|
|
|
|
/* On macOS 13 and above, the SCK based audio capture provides a
|
|
|
|
|
* better alternative to the device-based audio capture. */
|
|
|
|
|
if (__builtin_available(macOS 13.0, *)) {
|
|
|
|
|
hasDesktopAudio = false;
|
2021-01-14 17:34:21 +03:00
|
|
|
}
|
2016-07-06 01:35:12 -07:00
|
|
|
#endif
|
2014-08-31 16:28:36 +02:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
if (hasDesktopAudio)
|
|
|
|
|
ResetAudioDevice(App()->OutputAudioSource(), "default", Str("Basic.DesktopDevice1"), 1);
|
|
|
|
|
if (hasInputAudio)
|
|
|
|
|
ResetAudioDevice(App()->InputAudioSource(), "default", Str("Basic.AuxDevice1"), 3);
|
2014-07-27 12:48:14 -07:00
|
|
|
}
|
|
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
OBSSceneItem OBSBasic::GetSceneItem(QListWidgetItem *item)
|
2023-03-13 03:09:52 +01:00
|
|
|
{
|
2024-12-11 17:43:35 +01:00
|
|
|
return item ? GetOBSRef<OBSSceneItem>(item) : nullptr;
|
2023-03-13 03:09:52 +01:00
|
|
|
}
|
|
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
OBSSceneItem OBSBasic::GetCurrentSceneItem()
|
2019-02-06 13:30:02 -08:00
|
|
|
{
|
2024-12-11 17:43:35 +01:00
|
|
|
return ui->sources->Get(GetTopSelectedSourceItem());
|
2019-02-06 13:30:02 -08:00
|
|
|
}
|
|
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
static void RenameListValues(QListWidget *listWidget, const QString &newName, const QString &prevName)
|
2022-10-21 16:35:56 +02:00
|
|
|
{
|
2024-12-11 17:43:35 +01:00
|
|
|
QList<QListWidgetItem *> items = listWidget->findItems(prevName, Qt::MatchExactly);
|
2022-10-21 16:35:56 +02:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
for (int i = 0; i < items.count(); i++)
|
|
|
|
|
items[i]->setText(newName);
|
2022-10-21 16:35:56 +02:00
|
|
|
}
|
|
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
void OBSBasic::RenameSources(OBSSource source, QString newName, QString prevName)
|
2014-02-02 15:23:38 -07:00
|
|
|
{
|
2024-12-11 17:43:35 +01:00
|
|
|
RenameListValues(ui->scenes, newName, prevName);
|
2019-10-10 20:12:20 -05:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
if (vcamConfig.type == VCamOutputType::SourceOutput && prevName == QString::fromStdString(vcamConfig.source))
|
|
|
|
|
vcamConfig.source = newName.toStdString();
|
|
|
|
|
if (vcamConfig.type == VCamOutputType::SceneOutput && prevName == QString::fromStdString(vcamConfig.scene))
|
|
|
|
|
vcamConfig.scene = newName.toStdString();
|
2021-06-27 15:30:00 -07:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
SaveProject();
|
2022-08-18 07:50:24 -05:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
obs_scene_t *scene = obs_scene_from_source(source);
|
|
|
|
|
if (scene)
|
|
|
|
|
OBSProjector::UpdateMultiviewProjectors();
|
2023-02-08 03:57:51 -06:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
UpdateContextBar();
|
|
|
|
|
UpdatePreviewProgramIndicators();
|
2014-02-02 15:23:38 -07:00
|
|
|
}
|
|
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
void OBSBasic::GetAudioSourceFilters()
|
2014-05-03 22:54:38 -07:00
|
|
|
{
|
2024-12-11 17:43:35 +01:00
|
|
|
QAction *action = reinterpret_cast<QAction *>(sender());
|
|
|
|
|
VolControl *vol = action->property("volControl").value<VolControl *>();
|
|
|
|
|
obs_source_t *source = vol->GetSource();
|
2014-05-03 22:54:38 -07:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
CreateFiltersWindow(source);
|
2014-05-03 22:54:38 -07:00
|
|
|
}
|
|
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
void OBSBasic::GetAudioSourceProperties()
|
2014-04-26 23:47:50 -07:00
|
|
|
{
|
2024-12-11 17:43:35 +01:00
|
|
|
QAction *action = reinterpret_cast<QAction *>(sender());
|
|
|
|
|
VolControl *vol = action->property("volControl").value<VolControl *>();
|
|
|
|
|
obs_source_t *source = vol->GetSource();
|
2018-07-15 18:50:33 -07:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
CreatePropertiesWindow(source);
|
2014-04-26 23:47:50 -07:00
|
|
|
}
|
|
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
void OBSBasic::MixerRenameSource()
|
2015-02-17 02:45:34 -05:00
|
|
|
{
|
2024-12-11 17:43:35 +01:00
|
|
|
QAction *action = reinterpret_cast<QAction *>(sender());
|
|
|
|
|
VolControl *vol = action->property("volControl").value<VolControl *>();
|
|
|
|
|
OBSSource source = vol->GetSource();
|
2023-02-10 14:52:15 +03:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
const char *prevName = obs_source_get_name(source);
|
2015-02-17 02:45:34 -05:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
for (;;) {
|
|
|
|
|
string name;
|
|
|
|
|
bool accepted = NameDialog::AskForName(this, QTStr("Basic.Main.MixerRename.Title"),
|
|
|
|
|
QTStr("Basic.Main.MixerRename.Text"), name, QT_UTF8(prevName));
|
|
|
|
|
if (!accepted)
|
|
|
|
|
return;
|
2015-02-17 02:45:34 -05:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
if (name.empty()) {
|
|
|
|
|
OBSMessageBox::warning(this, QTStr("NoNameEntered.Title"), QTStr("NoNameEntered.Text"));
|
2023-02-10 14:52:15 +03:00
|
|
|
continue;
|
2015-02-17 02:45:34 -05:00
|
|
|
}
|
2022-07-25 07:22:24 -05:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
OBSSourceAutoRelease sourceTest = obs_get_source_by_name(name.c_str());
|
2022-07-25 07:22:24 -05:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
if (sourceTest) {
|
|
|
|
|
OBSMessageBox::warning(this, QTStr("NameExists.Title"), QTStr("NameExists.Text"));
|
2023-02-10 14:52:15 +03:00
|
|
|
continue;
|
2022-07-25 07:22:24 -05:00
|
|
|
}
|
2024-12-11 17:43:35 +01:00
|
|
|
|
|
|
|
|
obs_source_set_name(source, name.c_str());
|
|
|
|
|
break;
|
2022-07-25 07:22:24 -05:00
|
|
|
}
|
2015-02-17 02:45:34 -05:00
|
|
|
}
|
|
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
void OBSBasic::ActivateAudioSource(OBSSource source)
|
2018-01-09 18:45:20 -02:00
|
|
|
{
|
2024-12-11 17:43:35 +01:00
|
|
|
if (SourceMixerHidden(source))
|
|
|
|
|
return;
|
|
|
|
|
if (!obs_source_active(source))
|
|
|
|
|
return;
|
|
|
|
|
if (!obs_source_audio_active(source))
|
|
|
|
|
return;
|
2018-01-09 18:45:20 -02:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
bool vertical = config_get_bool(App()->GetUserConfig(), "BasicWindow", "VerticalVolControl");
|
|
|
|
|
VolControl *vol = new VolControl(source, true, vertical);
|
|
|
|
|
|
|
|
|
|
vol->EnableSlider(!SourceVolumeLocked(source));
|
|
|
|
|
|
|
|
|
|
double meterDecayRate = config_get_double(activeConfiguration, "Audio", "MeterDecayRate");
|
|
|
|
|
vol->SetMeterDecayRate(meterDecayRate);
|
2018-01-09 18:45:20 -02:00
|
|
|
|
2024-09-03 16:29:30 +02:00
|
|
|
uint32_t peakMeterTypeIdx = config_get_uint(activeConfiguration, "Audio", "PeakMeterType");
|
2018-01-19 22:57:36 +01:00
|
|
|
|
|
|
|
|
enum obs_peak_meter_type peakMeterType;
|
|
|
|
|
switch (peakMeterTypeIdx) {
|
|
|
|
|
case 0:
|
|
|
|
|
peakMeterType = SAMPLE_PEAK_METER;
|
|
|
|
|
break;
|
|
|
|
|
case 1:
|
|
|
|
|
peakMeterType = TRUE_PEAK_METER;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
peakMeterType = SAMPLE_PEAK_METER;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
vol->setPeakMeterType(peakMeterType);
|
2018-01-19 22:57:36 +01:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
vol->setContextMenuPolicy(Qt::CustomContextMenu);
|
2014-05-03 22:54:38 -07:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
connect(vol, &QWidget::customContextMenuRequested, this, &OBSBasic::VolControlContextMenu);
|
|
|
|
|
connect(vol, &VolControl::ConfigClicked, this, &OBSBasic::VolControlContextMenu);
|
2014-05-03 22:54:38 -07:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
InsertQObjectByName(volumes, vol);
|
|
|
|
|
|
|
|
|
|
for (auto volume : volumes) {
|
|
|
|
|
if (vertical)
|
|
|
|
|
ui->vVolControlLayout->addWidget(volume);
|
|
|
|
|
else
|
|
|
|
|
ui->hVolControlLayout->addWidget(volume);
|
2021-07-03 17:55:47 +12:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
void OBSBasic::DeactivateAudioSource(OBSSource source)
|
2015-06-25 03:53:48 -07:00
|
|
|
{
|
2024-12-11 17:43:35 +01:00
|
|
|
for (size_t i = 0; i < volumes.size(); i++) {
|
|
|
|
|
if (volumes[i]->GetSource() == source) {
|
|
|
|
|
delete volumes[i];
|
|
|
|
|
volumes.erase(volumes.begin() + i);
|
|
|
|
|
break;
|
|
|
|
|
}
|
2015-06-25 03:53:48 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
bool OBSBasic::QueryRemoveSource(obs_source_t *source)
|
2016-12-29 09:21:53 -06:00
|
|
|
{
|
2024-12-11 17:43:35 +01:00
|
|
|
if (obs_source_get_type(source) == OBS_SOURCE_TYPE_SCENE && !obs_source_is_group(source)) {
|
|
|
|
|
int count = ui->scenes->count();
|
2020-04-27 23:26:20 +02:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
if (count == 1) {
|
|
|
|
|
OBSMessageBox::information(this, QTStr("FinalScene.Title"), QTStr("FinalScene.Text"));
|
|
|
|
|
return false;
|
2018-03-01 02:10:32 -03:00
|
|
|
}
|
2024-12-11 17:43:35 +01:00
|
|
|
}
|
2020-04-27 23:26:20 +02:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
const char *name = obs_source_get_name(source);
|
2020-04-27 23:26:20 +02:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
QString text = QTStr("ConfirmRemove.Text").arg(QT_UTF8(name));
|
2017-11-08 22:38:44 -05:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
QMessageBox remove_source(this);
|
|
|
|
|
remove_source.setText(text);
|
|
|
|
|
QPushButton *Yes = remove_source.addButton(QTStr("Yes"), QMessageBox::YesRole);
|
|
|
|
|
remove_source.setDefaultButton(Yes);
|
|
|
|
|
remove_source.addButton(QTStr("No"), QMessageBox::NoRole);
|
|
|
|
|
remove_source.setIcon(QMessageBox::Question);
|
|
|
|
|
remove_source.setWindowTitle(QTStr("ConfirmRemove.Title"));
|
|
|
|
|
remove_source.exec();
|
2017-10-21 15:51:01 -02:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
return Yes == remove_source.clickedButton();
|
2017-10-21 15:51:01 -02:00
|
|
|
}
|
|
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
void OBSBasic::ReorderSources(OBSScene scene)
|
2014-04-26 23:47:50 -07:00
|
|
|
{
|
2024-12-11 17:43:35 +01:00
|
|
|
if (scene != GetCurrentScene() || ui->sources->IgnoreReorder())
|
|
|
|
|
return;
|
2024-04-23 05:05:50 +02:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
ui->sources->ReorderItems();
|
|
|
|
|
SaveProject();
|
2014-04-26 23:47:50 -07:00
|
|
|
}
|
|
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
void OBSBasic::RefreshSources(OBSScene scene)
|
2018-02-04 23:36:56 +02:00
|
|
|
{
|
2024-12-11 17:43:35 +01:00
|
|
|
if (scene != GetCurrentScene() || ui->sources->IgnoreReorder())
|
|
|
|
|
return;
|
2018-02-04 23:36:56 +02:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
ui->sources->RefreshItems();
|
|
|
|
|
SaveProject();
|
2018-02-04 23:36:56 +02:00
|
|
|
}
|
|
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
void OBSBasic::SourceCreated(void *data, calldata_t *params)
|
2014-05-03 22:54:38 -07:00
|
|
|
{
|
2024-12-11 17:43:35 +01:00
|
|
|
obs_source_t *source = (obs_source_t *)calldata_ptr(params, "source");
|
2020-04-07 21:11:29 +02:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
if (obs_scene_from_source(source) != NULL)
|
|
|
|
|
QMetaObject::invokeMethod(static_cast<OBSBasic *>(data), "AddScene", WaitConnection(),
|
|
|
|
|
Q_ARG(OBSSource, OBSSource(source)));
|
|
|
|
|
}
|
2014-05-03 22:54:38 -07:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
void OBSBasic::SourceRemoved(void *data, calldata_t *params)
|
|
|
|
|
{
|
|
|
|
|
obs_source_t *source = (obs_source_t *)calldata_ptr(params, "source");
|
2021-11-26 22:25:39 +13:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
if (obs_scene_from_source(source) != NULL)
|
|
|
|
|
QMetaObject::invokeMethod(static_cast<OBSBasic *>(data), "RemoveScene",
|
|
|
|
|
Q_ARG(OBSSource, OBSSource(source)));
|
2014-05-03 22:54:38 -07:00
|
|
|
}
|
|
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
void OBSBasic::SourceActivated(void *data, calldata_t *params)
|
2015-06-23 18:47:22 -07:00
|
|
|
{
|
2024-12-11 17:43:35 +01:00
|
|
|
obs_source_t *source = (obs_source_t *)calldata_ptr(params, "source");
|
|
|
|
|
uint32_t flags = obs_source_get_output_flags(source);
|
2015-06-23 18:47:22 -07:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
if (flags & OBS_SOURCE_AUDIO)
|
|
|
|
|
QMetaObject::invokeMethod(static_cast<OBSBasic *>(data), "ActivateAudioSource",
|
|
|
|
|
Q_ARG(OBSSource, OBSSource(source)));
|
|
|
|
|
}
|
2015-06-23 18:47:22 -07:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
void OBSBasic::SourceDeactivated(void *data, calldata_t *params)
|
|
|
|
|
{
|
|
|
|
|
obs_source_t *source = (obs_source_t *)calldata_ptr(params, "source");
|
|
|
|
|
uint32_t flags = obs_source_get_output_flags(source);
|
2015-06-23 18:47:22 -07:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
if (flags & OBS_SOURCE_AUDIO)
|
|
|
|
|
QMetaObject::invokeMethod(static_cast<OBSBasic *>(data), "DeactivateAudioSource",
|
|
|
|
|
Q_ARG(OBSSource, OBSSource(source)));
|
2015-06-23 18:47:22 -07:00
|
|
|
}
|
|
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
void OBSBasic::SourceAudioActivated(void *data, calldata_t *params)
|
2014-05-03 22:54:38 -07:00
|
|
|
{
|
2024-12-11 17:43:35 +01:00
|
|
|
obs_source_t *source = (obs_source_t *)calldata_ptr(params, "source");
|
2024-07-20 18:25:22 -05:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
if (obs_source_active(source))
|
|
|
|
|
QMetaObject::invokeMethod(static_cast<OBSBasic *>(data), "ActivateAudioSource",
|
|
|
|
|
Q_ARG(OBSSource, OBSSource(source)));
|
2015-09-11 21:49:21 -07:00
|
|
|
}
|
|
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
void OBSBasic::SourceAudioDeactivated(void *data, calldata_t *params)
|
2024-04-23 05:05:50 +02:00
|
|
|
{
|
2024-12-11 17:43:35 +01:00
|
|
|
obs_source_t *source = (obs_source_t *)calldata_ptr(params, "source");
|
|
|
|
|
QMetaObject::invokeMethod(static_cast<OBSBasic *>(data), "DeactivateAudioSource",
|
|
|
|
|
Q_ARG(OBSSource, OBSSource(source)));
|
2024-04-23 05:05:50 +02:00
|
|
|
}
|
|
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
void OBSBasic::SourceRenamed(void *data, calldata_t *params)
|
2015-09-11 21:49:21 -07:00
|
|
|
{
|
2024-12-11 17:43:35 +01:00
|
|
|
obs_source_t *source = (obs_source_t *)calldata_ptr(params, "source");
|
|
|
|
|
const char *newName = calldata_string(params, "new_name");
|
|
|
|
|
const char *prevName = calldata_string(params, "prev_name");
|
2015-09-11 21:49:21 -07:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
QMetaObject::invokeMethod(static_cast<OBSBasic *>(data), "RenameSources", Q_ARG(OBSSource, source),
|
|
|
|
|
Q_ARG(QString, QT_UTF8(newName)), Q_ARG(QString, QT_UTF8(prevName)));
|
2015-06-30 05:49:31 -07:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
blog(LOG_INFO, "Source '%s' renamed to '%s'", prevName, newName);
|
2014-05-03 22:54:38 -07:00
|
|
|
}
|
|
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
extern char *get_new_source_name(const char *name, const char *format);
|
|
|
|
|
|
|
|
|
|
void OBSBasic::ResetAudioDevice(const char *sourceId, const char *deviceId, const char *deviceDesc, int channel)
|
2015-06-25 03:53:48 -07:00
|
|
|
{
|
2024-12-11 17:43:35 +01:00
|
|
|
bool disable = deviceId && strcmp(deviceId, "disabled") == 0;
|
|
|
|
|
OBSSourceAutoRelease source;
|
|
|
|
|
OBSDataAutoRelease settings;
|
2015-06-25 03:53:48 -07:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
source = obs_get_output_source(channel);
|
|
|
|
|
if (source) {
|
|
|
|
|
if (disable) {
|
|
|
|
|
obs_set_output_source(channel, nullptr);
|
|
|
|
|
} else {
|
|
|
|
|
settings = obs_source_get_settings(source);
|
|
|
|
|
const char *oldId = obs_data_get_string(settings, "device_id");
|
|
|
|
|
if (strcmp(oldId, deviceId) != 0) {
|
|
|
|
|
obs_data_set_string(settings, "device_id", deviceId);
|
|
|
|
|
obs_source_update(source, settings);
|
2015-06-25 03:53:48 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
} else if (!disable) {
|
|
|
|
|
BPtr<char> name = get_new_source_name(deviceDesc, "%s (%d)");
|
2015-06-25 03:53:48 -07:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
settings = obs_data_create();
|
|
|
|
|
obs_data_set_string(settings, "device_id", deviceId);
|
|
|
|
|
source = obs_source_create(sourceId, name, settings, nullptr);
|
2015-06-25 03:53:48 -07:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
obs_set_output_source(channel, source);
|
2015-06-25 03:53:48 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
void OBSBasic::SetDeinterlacingMode()
|
2016-12-29 09:21:53 -06:00
|
|
|
{
|
2024-12-11 17:43:35 +01:00
|
|
|
QAction *action = reinterpret_cast<QAction *>(sender());
|
|
|
|
|
obs_deinterlace_mode mode = (obs_deinterlace_mode)action->property("mode").toInt();
|
|
|
|
|
OBSSceneItem sceneItem = GetCurrentSceneItem();
|
|
|
|
|
obs_source_t *source = obs_sceneitem_get_source(sceneItem);
|
2020-04-27 23:26:20 +02:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
obs_source_set_deinterlace_mode(source, mode);
|
2017-10-21 15:51:01 -02:00
|
|
|
}
|
|
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
void OBSBasic::SetDeinterlacingOrder()
|
2016-08-05 17:43:30 -07:00
|
|
|
{
|
2024-12-11 17:43:35 +01:00
|
|
|
QAction *action = reinterpret_cast<QAction *>(sender());
|
|
|
|
|
obs_deinterlace_field_order order = (obs_deinterlace_field_order)action->property("order").toInt();
|
|
|
|
|
OBSSceneItem sceneItem = GetCurrentSceneItem();
|
|
|
|
|
obs_source_t *source = obs_sceneitem_get_source(sceneItem);
|
2016-08-05 17:43:30 -07:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
obs_source_set_deinterlace_field_order(source, order);
|
2016-08-05 17:43:30 -07:00
|
|
|
}
|
|
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
QMenu *OBSBasic::AddDeinterlacingMenu(QMenu *menu, obs_source_t *source)
|
2016-08-05 17:43:30 -07:00
|
|
|
{
|
2024-12-11 17:43:35 +01:00
|
|
|
obs_deinterlace_mode deinterlaceMode = obs_source_get_deinterlace_mode(source);
|
|
|
|
|
obs_deinterlace_field_order deinterlaceOrder = obs_source_get_deinterlace_field_order(source);
|
|
|
|
|
QAction *action;
|
2019-03-31 21:30:09 +11:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
#define ADD_MODE(name, mode) \
|
|
|
|
|
action = menu->addAction(QTStr("" name), this, &OBSBasic::SetDeinterlacingMode); \
|
|
|
|
|
action->setProperty("mode", (int)mode); \
|
|
|
|
|
action->setCheckable(true); \
|
|
|
|
|
action->setChecked(deinterlaceMode == mode);
|
2016-08-05 17:43:30 -07:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
ADD_MODE("Disable", OBS_DEINTERLACE_MODE_DISABLE);
|
|
|
|
|
ADD_MODE("Deinterlacing.Discard", OBS_DEINTERLACE_MODE_DISCARD);
|
|
|
|
|
ADD_MODE("Deinterlacing.Retro", OBS_DEINTERLACE_MODE_RETRO);
|
|
|
|
|
ADD_MODE("Deinterlacing.Blend", OBS_DEINTERLACE_MODE_BLEND);
|
|
|
|
|
ADD_MODE("Deinterlacing.Blend2x", OBS_DEINTERLACE_MODE_BLEND_2X);
|
|
|
|
|
ADD_MODE("Deinterlacing.Linear", OBS_DEINTERLACE_MODE_LINEAR);
|
|
|
|
|
ADD_MODE("Deinterlacing.Linear2x", OBS_DEINTERLACE_MODE_LINEAR_2X);
|
|
|
|
|
ADD_MODE("Deinterlacing.Yadif", OBS_DEINTERLACE_MODE_YADIF);
|
|
|
|
|
ADD_MODE("Deinterlacing.Yadif2x", OBS_DEINTERLACE_MODE_YADIF_2X);
|
|
|
|
|
#undef ADD_MODE
|
2016-08-05 17:43:30 -07:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
menu->addSeparator();
|
2017-03-02 13:05:38 -08:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
#define ADD_ORDER(name, order) \
|
|
|
|
|
action = menu->addAction(QTStr("Deinterlacing." name), this, &OBSBasic::SetDeinterlacingOrder); \
|
|
|
|
|
action->setProperty("order", (int)order); \
|
|
|
|
|
action->setCheckable(true); \
|
|
|
|
|
action->setChecked(deinterlaceOrder == order);
|
2017-03-02 13:05:38 -08:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
ADD_ORDER("TopFieldFirst", OBS_DEINTERLACE_FIELD_ORDER_TOP);
|
|
|
|
|
ADD_ORDER("BottomFieldFirst", OBS_DEINTERLACE_FIELD_ORDER_BOTTOM);
|
|
|
|
|
#undef ADD_ORDER
|
2021-06-17 15:20:33 +10:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
return menu;
|
2016-08-05 17:43:30 -07:00
|
|
|
}
|
|
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
void OBSBasic::SetScaleFilter()
|
2016-08-05 17:43:30 -07:00
|
|
|
{
|
2024-12-11 17:43:35 +01:00
|
|
|
QAction *action = reinterpret_cast<QAction *>(sender());
|
|
|
|
|
obs_scale_type mode = (obs_scale_type)action->property("mode").toInt();
|
|
|
|
|
OBSSceneItem sceneItem = GetCurrentSceneItem();
|
2016-08-05 17:43:30 -07:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
obs_sceneitem_set_scale_filter(sceneItem, mode);
|
2016-08-05 17:43:30 -07:00
|
|
|
}
|
|
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
QMenu *OBSBasic::AddScaleFilteringMenu(QMenu *menu, obs_sceneitem_t *item)
|
2014-04-26 23:47:50 -07:00
|
|
|
{
|
2024-12-11 17:43:35 +01:00
|
|
|
obs_scale_type scaleFilter = obs_sceneitem_get_scale_filter(item);
|
|
|
|
|
QAction *action;
|
2014-04-26 23:47:50 -07:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
#define ADD_MODE(name, mode) \
|
|
|
|
|
action = menu->addAction(QTStr("" name), this, &OBSBasic::SetScaleFilter); \
|
|
|
|
|
action->setProperty("mode", (int)mode); \
|
|
|
|
|
action->setCheckable(true); \
|
|
|
|
|
action->setChecked(scaleFilter == mode);
|
2021-03-22 01:20:11 -04:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
ADD_MODE("Disable", OBS_SCALE_DISABLE);
|
|
|
|
|
ADD_MODE("ScaleFiltering.Point", OBS_SCALE_POINT);
|
|
|
|
|
ADD_MODE("ScaleFiltering.Bilinear", OBS_SCALE_BILINEAR);
|
|
|
|
|
ADD_MODE("ScaleFiltering.Bicubic", OBS_SCALE_BICUBIC);
|
|
|
|
|
ADD_MODE("ScaleFiltering.Lanczos", OBS_SCALE_LANCZOS);
|
|
|
|
|
ADD_MODE("ScaleFiltering.Area", OBS_SCALE_AREA);
|
|
|
|
|
#undef ADD_MODE
|
2022-07-20 03:44:43 -05:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
return menu;
|
2022-07-20 03:44:43 -05:00
|
|
|
}
|
|
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
void OBSBasic::SetBlendingMethod()
|
2024-04-23 05:05:50 +02:00
|
|
|
{
|
2024-12-11 17:43:35 +01:00
|
|
|
QAction *action = reinterpret_cast<QAction *>(sender());
|
|
|
|
|
obs_blending_method method = (obs_blending_method)action->property("method").toInt();
|
|
|
|
|
OBSSceneItem sceneItem = GetCurrentSceneItem();
|
2024-04-23 05:05:50 +02:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
obs_sceneitem_set_blending_method(sceneItem, method);
|
2024-04-23 05:05:50 +02:00
|
|
|
}
|
|
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
QMenu *OBSBasic::AddBlendingMethodMenu(QMenu *menu, obs_sceneitem_t *item)
|
2021-03-22 01:20:11 -04:00
|
|
|
{
|
2024-12-11 17:43:35 +01:00
|
|
|
obs_blending_method blendingMethod = obs_sceneitem_get_blending_method(item);
|
|
|
|
|
QAction *action;
|
2015-07-02 16:17:55 -07:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
#define ADD_MODE(name, method) \
|
|
|
|
|
action = menu->addAction(QTStr("" name), this, &OBSBasic::SetBlendingMethod); \
|
|
|
|
|
action->setProperty("method", (int)method); \
|
|
|
|
|
action->setCheckable(true); \
|
|
|
|
|
action->setChecked(blendingMethod == method);
|
2023-05-03 14:59:48 +02:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
ADD_MODE("BlendingMethod.Default", OBS_BLEND_METHOD_DEFAULT);
|
|
|
|
|
ADD_MODE("BlendingMethod.SrgbOff", OBS_BLEND_METHOD_SRGB_OFF);
|
|
|
|
|
#undef ADD_MODE
|
2023-05-03 14:59:48 +02:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
return menu;
|
|
|
|
|
}
|
2020-08-25 06:30:59 -07:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
void OBSBasic::SetBlendingMode()
|
|
|
|
|
{
|
|
|
|
|
QAction *action = reinterpret_cast<QAction *>(sender());
|
|
|
|
|
obs_blending_type mode = (obs_blending_type)action->property("mode").toInt();
|
|
|
|
|
OBSSceneItem sceneItem = GetCurrentSceneItem();
|
2020-08-25 06:30:59 -07:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
obs_sceneitem_set_blending_mode(sceneItem, mode);
|
|
|
|
|
}
|
2017-12-25 13:23:39 -08:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
QMenu *OBSBasic::AddBlendingModeMenu(QMenu *menu, obs_sceneitem_t *item)
|
|
|
|
|
{
|
|
|
|
|
obs_blending_type blendingMode = obs_sceneitem_get_blending_mode(item);
|
|
|
|
|
QAction *action;
|
2023-03-13 03:09:52 +01:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
#define ADD_MODE(name, mode) \
|
|
|
|
|
action = menu->addAction(QTStr("" name), this, &OBSBasic::SetBlendingMode); \
|
|
|
|
|
action->setProperty("mode", (int)mode); \
|
|
|
|
|
action->setCheckable(true); \
|
|
|
|
|
action->setChecked(blendingMode == mode);
|
UI: Implement transitions and preview/program mode
Implements transitions, and introduces "Studio Mode" which allows live
editing of the same or different scenes while preserving what's
currently being displayed.
Studio Mode offers a number of new features:
- The ability to edit different scenes or the same scene without
modifying what's currently being displayed (of course)
- The ability to set up "quick transitions" with a desired transition
and duration that can be assigned hotkeys
- The option to create full copies of all sources in the program scene
to allow editing of source properties of the same scene live without
modifying the output, or (by default) just use references. (Note
however that certain sources cannot be duplicated, such as capture
sources, media sources, and device sources)
- Swap Mode (enabled by default) which swaps the program scene with
the preview scene when a transition completes
Currently, only non-configurable transitions (transitions without
properties) are listed, and the only transitions available as of this
writing are fade and cut. In future versions more transitions will be
added, such as swipe, stingers, and many other various sort of
transitions, and the UI will support being able to add/configure/remove
those sort of configurable transitions.
2016-01-23 11:19:29 -08:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
ADD_MODE("BlendingMode.Normal", OBS_BLEND_NORMAL);
|
|
|
|
|
ADD_MODE("BlendingMode.Additive", OBS_BLEND_ADDITIVE);
|
|
|
|
|
ADD_MODE("BlendingMode.Subtract", OBS_BLEND_SUBTRACT);
|
|
|
|
|
ADD_MODE("BlendingMode.Screen", OBS_BLEND_SCREEN);
|
|
|
|
|
ADD_MODE("BlendingMode.Multiply", OBS_BLEND_MULTIPLY);
|
|
|
|
|
ADD_MODE("BlendingMode.Lighten", OBS_BLEND_LIGHTEN);
|
|
|
|
|
ADD_MODE("BlendingMode.Darken", OBS_BLEND_DARKEN);
|
|
|
|
|
#undef ADD_MODE
|
2016-04-13 18:59:27 -07:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
return menu;
|
|
|
|
|
}
|
UI: Implement transitions and preview/program mode
Implements transitions, and introduces "Studio Mode" which allows live
editing of the same or different scenes while preserving what's
currently being displayed.
Studio Mode offers a number of new features:
- The ability to edit different scenes or the same scene without
modifying what's currently being displayed (of course)
- The ability to set up "quick transitions" with a desired transition
and duration that can be assigned hotkeys
- The option to create full copies of all sources in the program scene
to allow editing of source properties of the same scene live without
modifying the output, or (by default) just use references. (Note
however that certain sources cannot be duplicated, such as capture
sources, media sources, and device sources)
- Swap Mode (enabled by default) which swaps the program scene with
the preview scene when a transition completes
Currently, only non-configurable transitions (transitions without
properties) are listed, and the only transitions available as of this
writing are fade and cut. In future versions more transitions will be
added, such as swipe, stingers, and many other various sort of
transitions, and the UI will support being able to add/configure/remove
those sort of configurable transitions.
2016-01-23 11:19:29 -08:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
QMenu *OBSBasic::AddBackgroundColorMenu(QMenu *menu, QWidgetAction *widgetAction, ColorSelect *select,
|
|
|
|
|
obs_sceneitem_t *item)
|
|
|
|
|
{
|
|
|
|
|
QAction *action;
|
2015-06-23 19:29:07 -07:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
menu->setStyleSheet(QString("*[bgColor=\"1\"]{background-color:rgba(255,68,68,33%);}"
|
|
|
|
|
"*[bgColor=\"2\"]{background-color:rgba(255,255,68,33%);}"
|
|
|
|
|
"*[bgColor=\"3\"]{background-color:rgba(68,255,68,33%);}"
|
|
|
|
|
"*[bgColor=\"4\"]{background-color:rgba(68,255,255,33%);}"
|
|
|
|
|
"*[bgColor=\"5\"]{background-color:rgba(68,68,255,33%);}"
|
|
|
|
|
"*[bgColor=\"6\"]{background-color:rgba(255,68,255,33%);}"
|
|
|
|
|
"*[bgColor=\"7\"]{background-color:rgba(68,68,68,33%);}"
|
|
|
|
|
"*[bgColor=\"8\"]{background-color:rgba(255,255,255,33%);}"));
|
2015-06-23 19:29:07 -07:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
obs_data_t *privData = obs_sceneitem_get_private_settings(item);
|
|
|
|
|
obs_data_release(privData);
|
2015-06-23 19:29:07 -07:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
obs_data_set_default_int(privData, "color-preset", 0);
|
|
|
|
|
int preset = obs_data_get_int(privData, "color-preset");
|
2014-04-26 23:47:50 -07:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
action = menu->addAction(QTStr("Clear"), this, &OBSBasic::ColorChange);
|
|
|
|
|
action->setCheckable(true);
|
|
|
|
|
action->setProperty("bgColor", 0);
|
|
|
|
|
action->setChecked(preset == 0);
|
2015-06-23 19:29:07 -07:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
action = menu->addAction(QTStr("CustomColor"), this, &OBSBasic::ColorChange);
|
|
|
|
|
action->setCheckable(true);
|
|
|
|
|
action->setProperty("bgColor", 1);
|
|
|
|
|
action->setChecked(preset == 1);
|
2014-05-03 22:54:38 -07:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
menu->addSeparator();
|
2018-07-15 18:50:33 -07:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
widgetAction->setDefaultWidget(select);
|
2024-04-23 05:05:50 +02:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
for (int i = 1; i < 9; i++) {
|
|
|
|
|
stringstream button;
|
|
|
|
|
button << "preset" << i;
|
|
|
|
|
QPushButton *colorButton = select->findChild<QPushButton *>(button.str().c_str());
|
|
|
|
|
if (preset == i + 1)
|
|
|
|
|
colorButton->setStyleSheet("border: 2px solid black");
|
2024-04-23 05:05:50 +02:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
colorButton->setProperty("bgColor", i);
|
|
|
|
|
select->connect(colorButton, &QPushButton::released, this, &OBSBasic::ColorChange);
|
2024-04-23 05:05:50 +02:00
|
|
|
}
|
|
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
menu->addAction(widgetAction);
|
2024-04-23 05:05:50 +02:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
return menu;
|
|
|
|
|
}
|
2024-04-23 05:05:50 +02:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
void OBSBasic::CreateSourcePopupMenu(int idx, bool preview)
|
|
|
|
|
{
|
|
|
|
|
QMenu popup(this);
|
|
|
|
|
delete previewProjectorSource;
|
|
|
|
|
delete sourceProjector;
|
|
|
|
|
delete scaleFilteringMenu;
|
|
|
|
|
delete blendingMethodMenu;
|
|
|
|
|
delete blendingModeMenu;
|
|
|
|
|
delete colorMenu;
|
|
|
|
|
delete colorWidgetAction;
|
|
|
|
|
delete colorSelect;
|
|
|
|
|
delete deinterlaceMenu;
|
2024-04-23 05:05:50 +02:00
|
|
|
|
2024-11-04 12:33:14 -05:00
|
|
|
OBSSceneItem sceneItem;
|
|
|
|
|
obs_source_t *source;
|
|
|
|
|
uint32_t flags;
|
|
|
|
|
bool isAsyncVideo = false;
|
|
|
|
|
bool hasAudio = false;
|
|
|
|
|
bool hasVideo = false;
|
|
|
|
|
|
|
|
|
|
bool sourceSelected = idx != -1;
|
|
|
|
|
|
|
|
|
|
if (sourceSelected) {
|
|
|
|
|
sceneItem = ui->sources->Get(idx);
|
|
|
|
|
source = obs_sceneitem_get_source(sceneItem);
|
|
|
|
|
flags = obs_source_get_output_flags(source);
|
|
|
|
|
isAsyncVideo = (flags & OBS_SOURCE_ASYNC_VIDEO) == OBS_SOURCE_ASYNC_VIDEO;
|
|
|
|
|
hasAudio = (flags & OBS_SOURCE_AUDIO) == OBS_SOURCE_AUDIO;
|
|
|
|
|
hasVideo = (flags & OBS_SOURCE_VIDEO) == OBS_SOURCE_VIDEO;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Add new source
|
|
|
|
|
QPointer<QMenu> addSourceMenu = CreateAddSourcePopupMenu();
|
|
|
|
|
if (addSourceMenu) {
|
|
|
|
|
popup.addMenu(addSourceMenu);
|
|
|
|
|
popup.addSeparator();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Preview menu entries
|
2024-12-11 17:43:35 +01:00
|
|
|
if (preview) {
|
|
|
|
|
QAction *action =
|
|
|
|
|
popup.addAction(QTStr("Basic.Main.PreviewConextMenu.Enable"), this, &OBSBasic::TogglePreview);
|
|
|
|
|
action->setCheckable(true);
|
|
|
|
|
action->setChecked(obs_display_enabled(ui->preview->GetDisplay()));
|
|
|
|
|
if (IsPreviewProgramMode())
|
|
|
|
|
action->setEnabled(false);
|
2024-04-23 05:05:50 +02:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
popup.addAction(ui->actionLockPreview);
|
|
|
|
|
popup.addMenu(ui->scalingMenu);
|
2014-04-26 23:47:50 -07:00
|
|
|
|
2024-11-04 12:33:14 -05:00
|
|
|
popup.addSeparator();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Projector menu entries
|
|
|
|
|
if (preview) {
|
|
|
|
|
previewProjectorSource = new QMenu(QTStr("Projector.Open.Preview"));
|
2024-12-11 17:43:35 +01:00
|
|
|
AddProjectorMenuMonitors(previewProjectorSource, this, &OBSBasic::OpenPreviewProjector);
|
2024-11-04 12:33:14 -05:00
|
|
|
previewProjectorSource->addSeparator();
|
|
|
|
|
previewProjectorSource->addAction(QTStr("Projector.Window"), this, &OBSBasic::OpenPreviewWindow);
|
2015-06-25 03:53:48 -07:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
popup.addMenu(previewProjectorSource);
|
2024-11-04 12:33:14 -05:00
|
|
|
}
|
UI: Implement transitions and preview/program mode
Implements transitions, and introduces "Studio Mode" which allows live
editing of the same or different scenes while preserving what's
currently being displayed.
Studio Mode offers a number of new features:
- The ability to edit different scenes or the same scene without
modifying what's currently being displayed (of course)
- The ability to set up "quick transitions" with a desired transition
and duration that can be assigned hotkeys
- The option to create full copies of all sources in the program scene
to allow editing of source properties of the same scene live without
modifying the output, or (by default) just use references. (Note
however that certain sources cannot be duplicated, such as capture
sources, media sources, and device sources)
- Swap Mode (enabled by default) which swaps the program scene with
the preview scene when a transition completes
Currently, only non-configurable transitions (transitions without
properties) are listed, and the only transitions available as of this
writing are fade and cut. In future versions more transitions will be
added, such as swipe, stingers, and many other various sort of
transitions, and the UI will support being able to add/configure/remove
those sort of configurable transitions.
2016-01-23 11:19:29 -08:00
|
|
|
|
2024-11-04 12:33:14 -05:00
|
|
|
if (hasVideo) {
|
|
|
|
|
sourceProjector = new QMenu(QTStr("Projector.Open.Source"));
|
|
|
|
|
AddProjectorMenuMonitors(sourceProjector, this, &OBSBasic::OpenSourceProjector);
|
|
|
|
|
sourceProjector->addSeparator();
|
|
|
|
|
sourceProjector->addAction(QTStr("Projector.Window"), this, &OBSBasic::OpenSourceWindow);
|
2016-04-13 18:59:27 -07:00
|
|
|
|
2024-11-04 12:33:14 -05:00
|
|
|
popup.addMenu(sourceProjector);
|
2023-11-30 20:31:34 +01:00
|
|
|
}
|
|
|
|
|
|
2024-11-04 12:33:14 -05:00
|
|
|
popup.addSeparator();
|
2018-02-27 20:26:22 -03:00
|
|
|
|
2024-11-04 12:33:14 -05:00
|
|
|
// Screenshot menu entries
|
|
|
|
|
if (preview) {
|
|
|
|
|
popup.addAction(QTStr("Screenshot.Preview"), this, &OBSBasic::ScreenshotScene);
|
2018-02-27 20:26:22 -03:00
|
|
|
}
|
|
|
|
|
|
2024-11-04 12:33:14 -05:00
|
|
|
if (hasVideo) {
|
|
|
|
|
popup.addAction(QTStr("Screenshot.Source"), this, &OBSBasic::ScreenshotSelectedSource);
|
|
|
|
|
}
|
2018-02-27 20:26:22 -03:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
popup.addSeparator();
|
2015-06-23 19:29:07 -07:00
|
|
|
|
2024-11-04 12:33:14 -05:00
|
|
|
if (sourceSelected) {
|
|
|
|
|
// Sources list menu entries
|
|
|
|
|
if (!preview) {
|
|
|
|
|
colorMenu = new QMenu(QTStr("ChangeBG"));
|
|
|
|
|
colorWidgetAction = new QWidgetAction(colorMenu);
|
|
|
|
|
colorSelect = new ColorSelect(colorMenu);
|
|
|
|
|
popup.addMenu(AddBackgroundColorMenu(colorMenu, colorWidgetAction, colorSelect, sceneItem));
|
|
|
|
|
|
|
|
|
|
if (hasAudio) {
|
|
|
|
|
QAction *actionHideMixer =
|
|
|
|
|
popup.addAction(QTStr("HideMixer"), this, &OBSBasic::ToggleHideMixer);
|
|
|
|
|
actionHideMixer->setCheckable(true);
|
|
|
|
|
actionHideMixer->setChecked(SourceMixerHidden(source));
|
|
|
|
|
}
|
2024-12-11 17:43:35 +01:00
|
|
|
popup.addSeparator();
|
2024-11-04 12:33:14 -05:00
|
|
|
}
|
2015-06-23 19:29:07 -07:00
|
|
|
|
2024-11-04 12:33:14 -05:00
|
|
|
// Scene item menu entries
|
|
|
|
|
if (hasVideo && source) {
|
|
|
|
|
scaleFilteringMenu = new QMenu(QTStr("ScaleFiltering"));
|
|
|
|
|
popup.addMenu(AddScaleFilteringMenu(scaleFilteringMenu, sceneItem));
|
|
|
|
|
blendingModeMenu = new QMenu(QTStr("BlendingMode"));
|
|
|
|
|
popup.addMenu(AddBlendingModeMenu(blendingModeMenu, sceneItem));
|
|
|
|
|
blendingMethodMenu = new QMenu(QTStr("BlendingMethod"));
|
|
|
|
|
popup.addMenu(AddBlendingMethodMenu(blendingMethodMenu, sceneItem));
|
|
|
|
|
if (isAsyncVideo) {
|
|
|
|
|
deinterlaceMenu = new QMenu(QTStr("Deinterlacing"));
|
|
|
|
|
popup.addMenu(AddDeinterlacingMenu(deinterlaceMenu, source));
|
|
|
|
|
}
|
2017-05-13 13:13:55 -04:00
|
|
|
|
2024-11-04 12:33:14 -05:00
|
|
|
popup.addMenu(CreateVisibilityTransitionMenu(true));
|
|
|
|
|
popup.addMenu(CreateVisibilityTransitionMenu(false));
|
2017-05-13 13:13:55 -04:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
popup.addSeparator();
|
2024-07-31 10:53:59 -04:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
QAction *resizeOutput = popup.addAction(QTStr("ResizeOutputSizeOfSource"), this,
|
|
|
|
|
&OBSBasic::ResizeOutputSizeOfSource);
|
2016-11-05 12:48:46 -04:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
int width = obs_source_get_width(source);
|
|
|
|
|
int height = obs_source_get_height(source);
|
2022-08-22 16:04:13 +02:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
resizeOutput->setEnabled(!obs_video_active());
|
2022-11-15 10:00:41 +01:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
if (width < 32 || height < 32)
|
|
|
|
|
resizeOutput->setEnabled(false);
|
2024-11-04 12:33:14 -05:00
|
|
|
}
|
2022-11-15 10:00:41 +01:00
|
|
|
|
2024-11-04 12:33:14 -05:00
|
|
|
popup.addSeparator();
|
|
|
|
|
|
|
|
|
|
popup.addMenu(ui->orderMenu);
|
|
|
|
|
|
|
|
|
|
if (hasVideo) {
|
|
|
|
|
popup.addMenu(ui->transformMenu);
|
|
|
|
|
}
|
2022-08-18 10:50:13 +02:00
|
|
|
|
2024-11-04 12:33:14 -05:00
|
|
|
popup.addSeparator();
|
|
|
|
|
|
|
|
|
|
// Source grouping
|
|
|
|
|
if (ui->sources->MultipleBaseSelected()) {
|
2024-12-11 17:43:35 +01:00
|
|
|
popup.addSeparator();
|
2024-11-04 12:33:14 -05:00
|
|
|
popup.addAction(QTStr("Basic.Main.GroupItems"), ui->sources, &SourceTree::GroupSelectedItems);
|
2024-06-01 18:42:34 +02:00
|
|
|
|
2024-11-04 12:33:14 -05:00
|
|
|
} else if (ui->sources->GroupsSelected()) {
|
2024-12-11 17:43:35 +01:00
|
|
|
popup.addSeparator();
|
2024-11-04 12:33:14 -05:00
|
|
|
popup.addAction(QTStr("Basic.Main.Ungroup"), ui->sources, &SourceTree::UngroupSelectedGroups);
|
|
|
|
|
}
|
|
|
|
|
popup.addSeparator();
|
2016-11-05 12:48:46 -04:00
|
|
|
|
2024-11-04 12:33:14 -05:00
|
|
|
popup.addAction(ui->actionCopySource);
|
|
|
|
|
popup.addAction(ui->actionPasteRef);
|
|
|
|
|
popup.addAction(ui->actionPasteDup);
|
|
|
|
|
popup.addSeparator();
|
2016-08-28 14:24:14 -07:00
|
|
|
|
2024-11-04 12:33:14 -05:00
|
|
|
if (hasVideo || hasAudio) {
|
|
|
|
|
popup.addAction(ui->actionCopyFilters);
|
|
|
|
|
popup.addAction(ui->actionPasteFilters);
|
|
|
|
|
popup.addSeparator();
|
2024-12-11 17:43:35 +01:00
|
|
|
}
|
2015-06-30 05:49:31 -07:00
|
|
|
|
2024-11-04 12:33:14 -05:00
|
|
|
popup.addAction(ui->actionRemoveSource);
|
|
|
|
|
popup.addAction(renameSource);
|
2024-12-11 17:43:35 +01:00
|
|
|
popup.addSeparator();
|
2016-04-13 18:59:27 -07:00
|
|
|
|
2024-11-04 12:33:14 -05:00
|
|
|
if (flags && flags & OBS_SOURCE_INTERACTION)
|
2024-12-11 17:43:35 +01:00
|
|
|
popup.addAction(QTStr("Interact"), this, &OBSBasic::on_actionInteract_triggered);
|
2016-04-13 19:00:12 -07:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
popup.addAction(QTStr("Filters"), this, [&]() { OpenFilters(); });
|
|
|
|
|
QAction *action =
|
|
|
|
|
popup.addAction(QTStr("Properties"), this, &OBSBasic::on_actionSourceProperties_triggered);
|
|
|
|
|
action->setEnabled(obs_source_configurable(source));
|
2024-11-04 12:33:14 -05:00
|
|
|
} else {
|
|
|
|
|
popup.addAction(ui->actionPasteRef);
|
|
|
|
|
popup.addAction(ui->actionPasteDup);
|
2016-04-13 19:00:12 -07:00
|
|
|
}
|
|
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
popup.exec(QCursor::pos());
|
|
|
|
|
}
|
2017-01-11 13:19:17 -06:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
void OBSBasic::on_sources_customContextMenuRequested(const QPoint &pos)
|
|
|
|
|
{
|
|
|
|
|
if (ui->scenes->count()) {
|
|
|
|
|
QModelIndex idx = ui->sources->indexAt(pos);
|
|
|
|
|
CreateSourcePopupMenu(idx.row(), false);
|
2020-06-20 07:42:14 -07:00
|
|
|
}
|
2014-04-26 23:47:50 -07:00
|
|
|
}
|
|
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
static inline bool should_show_properties(obs_source_t *source, const char *id)
|
obs-studio UI: Implement stream settings UI
- Updated the services API so that it links up with an output and
the output gets data from that service rather than via settings.
This allows the service context to have control over how an output is
used, and makes it so that the URL/key/etc isn't necessarily some
static setting.
Also, if the service is attached to an output, it will stick around
until the output is destroyed.
- The settings interface has been updated so that it can allow the
usage of service plugins. What this means is that now you can create
a service plugin that can control aspects of the stream, and it
allows each service to create their own user interface if they create
a service plugin module.
- Testing out saving of current service information. Saves/loads from
JSON in to obs_data_t, seems to be working quite nicely, and the
service object information is saved/preserved on exit, and loaded
again on startup.
- I agonized over the settings user interface for days, and eventually
I just decided that the only way that users weren't going to be
fumbling over options was to split up the settings in to simple/basic
output, pre-configured, and then advanced for advanced use (such as
multiple outputs or services, which I'll implement later).
This was particularly painful to really design right, I wanted more
features and wanted to include everything in one interface but
ultimately just realized from experience that users are just not
technically knowledgable about it and will end up fumbling with the
settings rather than getting things done.
Basically, what this means is that casual users only have to enter in
about 3 things to configure their stream: Stream key, audio bitrate,
and video bitrate. I am really happy with this interface for those
types of users, but it definitely won't be sufficient for advanced
usage or for custom outputs, so that stuff will have to be separated.
- Improved the JSON usage for the 'common streaming services' context,
I realized that JSON arrays are there to ensure sorting, while
forgetting that general items are optimized for hashing. So
basically I'm just using arrays now to sort items in it.
2014-04-24 01:49:07 -07:00
|
|
|
{
|
2024-12-11 17:43:35 +01:00
|
|
|
if (!source)
|
|
|
|
|
return false;
|
|
|
|
|
if (strcmp(id, "group") == 0)
|
|
|
|
|
return false;
|
|
|
|
|
if (!obs_source_configurable(source))
|
|
|
|
|
return false;
|
obs-studio UI: Implement stream settings UI
- Updated the services API so that it links up with an output and
the output gets data from that service rather than via settings.
This allows the service context to have control over how an output is
used, and makes it so that the URL/key/etc isn't necessarily some
static setting.
Also, if the service is attached to an output, it will stick around
until the output is destroyed.
- The settings interface has been updated so that it can allow the
usage of service plugins. What this means is that now you can create
a service plugin that can control aspects of the stream, and it
allows each service to create their own user interface if they create
a service plugin module.
- Testing out saving of current service information. Saves/loads from
JSON in to obs_data_t, seems to be working quite nicely, and the
service object information is saved/preserved on exit, and loaded
again on startup.
- I agonized over the settings user interface for days, and eventually
I just decided that the only way that users weren't going to be
fumbling over options was to split up the settings in to simple/basic
output, pre-configured, and then advanced for advanced use (such as
multiple outputs or services, which I'll implement later).
This was particularly painful to really design right, I wanted more
features and wanted to include everything in one interface but
ultimately just realized from experience that users are just not
technically knowledgable about it and will end up fumbling with the
settings rather than getting things done.
Basically, what this means is that casual users only have to enter in
about 3 things to configure their stream: Stream key, audio bitrate,
and video bitrate. I am really happy with this interface for those
types of users, but it definitely won't be sufficient for advanced
usage or for custom outputs, so that stuff will have to be separated.
- Improved the JSON usage for the 'common streaming services' context,
I realized that JSON arrays are there to ensure sorting, while
forgetting that general items are optimized for hashing. So
basically I'm just using arrays now to sort items in it.
2014-04-24 01:49:07 -07:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
uint32_t caps = obs_source_get_output_flags(source);
|
|
|
|
|
if ((caps & OBS_SOURCE_CAP_DONT_SHOW_PROPERTIES) != 0)
|
|
|
|
|
return false;
|
obs-studio UI: Implement stream settings UI
- Updated the services API so that it links up with an output and
the output gets data from that service rather than via settings.
This allows the service context to have control over how an output is
used, and makes it so that the URL/key/etc isn't necessarily some
static setting.
Also, if the service is attached to an output, it will stick around
until the output is destroyed.
- The settings interface has been updated so that it can allow the
usage of service plugins. What this means is that now you can create
a service plugin that can control aspects of the stream, and it
allows each service to create their own user interface if they create
a service plugin module.
- Testing out saving of current service information. Saves/loads from
JSON in to obs_data_t, seems to be working quite nicely, and the
service object information is saved/preserved on exit, and loaded
again on startup.
- I agonized over the settings user interface for days, and eventually
I just decided that the only way that users weren't going to be
fumbling over options was to split up the settings in to simple/basic
output, pre-configured, and then advanced for advanced use (such as
multiple outputs or services, which I'll implement later).
This was particularly painful to really design right, I wanted more
features and wanted to include everything in one interface but
ultimately just realized from experience that users are just not
technically knowledgable about it and will end up fumbling with the
settings rather than getting things done.
Basically, what this means is that casual users only have to enter in
about 3 things to configure their stream: Stream key, audio bitrate,
and video bitrate. I am really happy with this interface for those
types of users, but it definitely won't be sufficient for advanced
usage or for custom outputs, so that stuff will have to be separated.
- Improved the JSON usage for the 'common streaming services' context,
I realized that JSON arrays are there to ensure sorting, while
forgetting that general items are optimized for hashing. So
basically I'm just using arrays now to sort items in it.
2014-04-24 01:49:07 -07:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
return true;
|
|
|
|
|
}
|
obs-studio UI: Implement stream settings UI
- Updated the services API so that it links up with an output and
the output gets data from that service rather than via settings.
This allows the service context to have control over how an output is
used, and makes it so that the URL/key/etc isn't necessarily some
static setting.
Also, if the service is attached to an output, it will stick around
until the output is destroyed.
- The settings interface has been updated so that it can allow the
usage of service plugins. What this means is that now you can create
a service plugin that can control aspects of the stream, and it
allows each service to create their own user interface if they create
a service plugin module.
- Testing out saving of current service information. Saves/loads from
JSON in to obs_data_t, seems to be working quite nicely, and the
service object information is saved/preserved on exit, and loaded
again on startup.
- I agonized over the settings user interface for days, and eventually
I just decided that the only way that users weren't going to be
fumbling over options was to split up the settings in to simple/basic
output, pre-configured, and then advanced for advanced use (such as
multiple outputs or services, which I'll implement later).
This was particularly painful to really design right, I wanted more
features and wanted to include everything in one interface but
ultimately just realized from experience that users are just not
technically knowledgable about it and will end up fumbling with the
settings rather than getting things done.
Basically, what this means is that casual users only have to enter in
about 3 things to configure their stream: Stream key, audio bitrate,
and video bitrate. I am really happy with this interface for those
types of users, but it definitely won't be sufficient for advanced
usage or for custom outputs, so that stuff will have to be separated.
- Improved the JSON usage for the 'common streaming services' context,
I realized that JSON arrays are there to ensure sorting, while
forgetting that general items are optimized for hashing. So
basically I'm just using arrays now to sort items in it.
2014-04-24 01:49:07 -07:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
void OBSBasic::AddSource(const char *id)
|
|
|
|
|
{
|
|
|
|
|
if (id && *id) {
|
|
|
|
|
OBSBasicSourceSelect sourceSelect(this, id, undo_s);
|
|
|
|
|
sourceSelect.exec();
|
|
|
|
|
if (should_show_properties(sourceSelect.newSource, id)) {
|
|
|
|
|
CreatePropertiesWindow(sourceSelect.newSource);
|
|
|
|
|
}
|
2024-09-03 16:29:30 +02:00
|
|
|
}
|
obs-studio UI: Implement stream settings UI
- Updated the services API so that it links up with an output and
the output gets data from that service rather than via settings.
This allows the service context to have control over how an output is
used, and makes it so that the URL/key/etc isn't necessarily some
static setting.
Also, if the service is attached to an output, it will stick around
until the output is destroyed.
- The settings interface has been updated so that it can allow the
usage of service plugins. What this means is that now you can create
a service plugin that can control aspects of the stream, and it
allows each service to create their own user interface if they create
a service plugin module.
- Testing out saving of current service information. Saves/loads from
JSON in to obs_data_t, seems to be working quite nicely, and the
service object information is saved/preserved on exit, and loaded
again on startup.
- I agonized over the settings user interface for days, and eventually
I just decided that the only way that users weren't going to be
fumbling over options was to split up the settings in to simple/basic
output, pre-configured, and then advanced for advanced use (such as
multiple outputs or services, which I'll implement later).
This was particularly painful to really design right, I wanted more
features and wanted to include everything in one interface but
ultimately just realized from experience that users are just not
technically knowledgable about it and will end up fumbling with the
settings rather than getting things done.
Basically, what this means is that casual users only have to enter in
about 3 things to configure their stream: Stream key, audio bitrate,
and video bitrate. I am really happy with this interface for those
types of users, but it definitely won't be sufficient for advanced
usage or for custom outputs, so that stuff will have to be separated.
- Improved the JSON usage for the 'common streaming services' context,
I realized that JSON arrays are there to ensure sorting, while
forgetting that general items are optimized for hashing. So
basically I'm just using arrays now to sort items in it.
2014-04-24 01:49:07 -07:00
|
|
|
}
|
|
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
QMenu *OBSBasic::CreateAddSourcePopupMenu()
|
obs-studio UI: Implement stream settings UI
- Updated the services API so that it links up with an output and
the output gets data from that service rather than via settings.
This allows the service context to have control over how an output is
used, and makes it so that the URL/key/etc isn't necessarily some
static setting.
Also, if the service is attached to an output, it will stick around
until the output is destroyed.
- The settings interface has been updated so that it can allow the
usage of service plugins. What this means is that now you can create
a service plugin that can control aspects of the stream, and it
allows each service to create their own user interface if they create
a service plugin module.
- Testing out saving of current service information. Saves/loads from
JSON in to obs_data_t, seems to be working quite nicely, and the
service object information is saved/preserved on exit, and loaded
again on startup.
- I agonized over the settings user interface for days, and eventually
I just decided that the only way that users weren't going to be
fumbling over options was to split up the settings in to simple/basic
output, pre-configured, and then advanced for advanced use (such as
multiple outputs or services, which I'll implement later).
This was particularly painful to really design right, I wanted more
features and wanted to include everything in one interface but
ultimately just realized from experience that users are just not
technically knowledgable about it and will end up fumbling with the
settings rather than getting things done.
Basically, what this means is that casual users only have to enter in
about 3 things to configure their stream: Stream key, audio bitrate,
and video bitrate. I am really happy with this interface for those
types of users, but it definitely won't be sufficient for advanced
usage or for custom outputs, so that stuff will have to be separated.
- Improved the JSON usage for the 'common streaming services' context,
I realized that JSON arrays are there to ensure sorting, while
forgetting that general items are optimized for hashing. So
basically I'm just using arrays now to sort items in it.
2014-04-24 01:49:07 -07:00
|
|
|
{
|
2024-12-11 17:43:35 +01:00
|
|
|
const char *unversioned_type;
|
|
|
|
|
const char *type;
|
|
|
|
|
bool foundValues = false;
|
|
|
|
|
bool foundDeprecated = false;
|
|
|
|
|
size_t idx = 0;
|
obs-studio UI: Implement stream settings UI
- Updated the services API so that it links up with an output and
the output gets data from that service rather than via settings.
This allows the service context to have control over how an output is
used, and makes it so that the URL/key/etc isn't necessarily some
static setting.
Also, if the service is attached to an output, it will stick around
until the output is destroyed.
- The settings interface has been updated so that it can allow the
usage of service plugins. What this means is that now you can create
a service plugin that can control aspects of the stream, and it
allows each service to create their own user interface if they create
a service plugin module.
- Testing out saving of current service information. Saves/loads from
JSON in to obs_data_t, seems to be working quite nicely, and the
service object information is saved/preserved on exit, and loaded
again on startup.
- I agonized over the settings user interface for days, and eventually
I just decided that the only way that users weren't going to be
fumbling over options was to split up the settings in to simple/basic
output, pre-configured, and then advanced for advanced use (such as
multiple outputs or services, which I'll implement later).
This was particularly painful to really design right, I wanted more
features and wanted to include everything in one interface but
ultimately just realized from experience that users are just not
technically knowledgable about it and will end up fumbling with the
settings rather than getting things done.
Basically, what this means is that casual users only have to enter in
about 3 things to configure their stream: Stream key, audio bitrate,
and video bitrate. I am really happy with this interface for those
types of users, but it definitely won't be sufficient for advanced
usage or for custom outputs, so that stuff will have to be separated.
- Improved the JSON usage for the 'common streaming services' context,
I realized that JSON arrays are there to ensure sorting, while
forgetting that general items are optimized for hashing. So
basically I'm just using arrays now to sort items in it.
2014-04-24 01:49:07 -07:00
|
|
|
|
2025-04-13 12:45:17 -07:00
|
|
|
QMenu *popup = new QMenu(QTStr("AddSource"), this);
|
2024-12-11 17:43:35 +01:00
|
|
|
QMenu *deprecated = new QMenu(QTStr("Deprecated"), popup);
|
obs-studio UI: Implement stream settings UI
- Updated the services API so that it links up with an output and
the output gets data from that service rather than via settings.
This allows the service context to have control over how an output is
used, and makes it so that the URL/key/etc isn't necessarily some
static setting.
Also, if the service is attached to an output, it will stick around
until the output is destroyed.
- The settings interface has been updated so that it can allow the
usage of service plugins. What this means is that now you can create
a service plugin that can control aspects of the stream, and it
allows each service to create their own user interface if they create
a service plugin module.
- Testing out saving of current service information. Saves/loads from
JSON in to obs_data_t, seems to be working quite nicely, and the
service object information is saved/preserved on exit, and loaded
again on startup.
- I agonized over the settings user interface for days, and eventually
I just decided that the only way that users weren't going to be
fumbling over options was to split up the settings in to simple/basic
output, pre-configured, and then advanced for advanced use (such as
multiple outputs or services, which I'll implement later).
This was particularly painful to really design right, I wanted more
features and wanted to include everything in one interface but
ultimately just realized from experience that users are just not
technically knowledgable about it and will end up fumbling with the
settings rather than getting things done.
Basically, what this means is that casual users only have to enter in
about 3 things to configure their stream: Stream key, audio bitrate,
and video bitrate. I am really happy with this interface for those
types of users, but it definitely won't be sufficient for advanced
usage or for custom outputs, so that stuff will have to be separated.
- Improved the JSON usage for the 'common streaming services' context,
I realized that JSON arrays are there to ensure sorting, while
forgetting that general items are optimized for hashing. So
basically I'm just using arrays now to sort items in it.
2014-04-24 01:49:07 -07:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
auto getActionAfter = [](QMenu *menu, const QString &name) {
|
|
|
|
|
QList<QAction *> actions = menu->actions();
|
2024-09-03 16:29:30 +02:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
for (QAction *menuAction : actions) {
|
|
|
|
|
if (menuAction->text().compare(name, Qt::CaseInsensitive) >= 0)
|
|
|
|
|
return menuAction;
|
2024-09-03 16:29:30 +02:00
|
|
|
}
|
2020-06-18 23:45:52 +02:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
return (QAction *)nullptr;
|
|
|
|
|
};
|
obs-studio UI: Implement stream settings UI
- Updated the services API so that it links up with an output and
the output gets data from that service rather than via settings.
This allows the service context to have control over how an output is
used, and makes it so that the URL/key/etc isn't necessarily some
static setting.
Also, if the service is attached to an output, it will stick around
until the output is destroyed.
- The settings interface has been updated so that it can allow the
usage of service plugins. What this means is that now you can create
a service plugin that can control aspects of the stream, and it
allows each service to create their own user interface if they create
a service plugin module.
- Testing out saving of current service information. Saves/loads from
JSON in to obs_data_t, seems to be working quite nicely, and the
service object information is saved/preserved on exit, and loaded
again on startup.
- I agonized over the settings user interface for days, and eventually
I just decided that the only way that users weren't going to be
fumbling over options was to split up the settings in to simple/basic
output, pre-configured, and then advanced for advanced use (such as
multiple outputs or services, which I'll implement later).
This was particularly painful to really design right, I wanted more
features and wanted to include everything in one interface but
ultimately just realized from experience that users are just not
technically knowledgable about it and will end up fumbling with the
settings rather than getting things done.
Basically, what this means is that casual users only have to enter in
about 3 things to configure their stream: Stream key, audio bitrate,
and video bitrate. I am really happy with this interface for those
types of users, but it definitely won't be sufficient for advanced
usage or for custom outputs, so that stuff will have to be separated.
- Improved the JSON usage for the 'common streaming services' context,
I realized that JSON arrays are there to ensure sorting, while
forgetting that general items are optimized for hashing. So
basically I'm just using arrays now to sort items in it.
2014-04-24 01:49:07 -07:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
auto addSource = [this, getActionAfter](QMenu *popup, const char *type, const char *name) {
|
|
|
|
|
QString qname = QT_UTF8(name);
|
|
|
|
|
QAction *popupItem = new QAction(qname, this);
|
|
|
|
|
connect(popupItem, &QAction::triggered, [this, type]() { AddSource(type); });
|
obs-studio UI: Implement stream settings UI
- Updated the services API so that it links up with an output and
the output gets data from that service rather than via settings.
This allows the service context to have control over how an output is
used, and makes it so that the URL/key/etc isn't necessarily some
static setting.
Also, if the service is attached to an output, it will stick around
until the output is destroyed.
- The settings interface has been updated so that it can allow the
usage of service plugins. What this means is that now you can create
a service plugin that can control aspects of the stream, and it
allows each service to create their own user interface if they create
a service plugin module.
- Testing out saving of current service information. Saves/loads from
JSON in to obs_data_t, seems to be working quite nicely, and the
service object information is saved/preserved on exit, and loaded
again on startup.
- I agonized over the settings user interface for days, and eventually
I just decided that the only way that users weren't going to be
fumbling over options was to split up the settings in to simple/basic
output, pre-configured, and then advanced for advanced use (such as
multiple outputs or services, which I'll implement later).
This was particularly painful to really design right, I wanted more
features and wanted to include everything in one interface but
ultimately just realized from experience that users are just not
technically knowledgable about it and will end up fumbling with the
settings rather than getting things done.
Basically, what this means is that casual users only have to enter in
about 3 things to configure their stream: Stream key, audio bitrate,
and video bitrate. I am really happy with this interface for those
types of users, but it definitely won't be sufficient for advanced
usage or for custom outputs, so that stuff will have to be separated.
- Improved the JSON usage for the 'common streaming services' context,
I realized that JSON arrays are there to ensure sorting, while
forgetting that general items are optimized for hashing. So
basically I'm just using arrays now to sort items in it.
2014-04-24 01:49:07 -07:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
QIcon icon;
|
obs-studio UI: Implement stream settings UI
- Updated the services API so that it links up with an output and
the output gets data from that service rather than via settings.
This allows the service context to have control over how an output is
used, and makes it so that the URL/key/etc isn't necessarily some
static setting.
Also, if the service is attached to an output, it will stick around
until the output is destroyed.
- The settings interface has been updated so that it can allow the
usage of service plugins. What this means is that now you can create
a service plugin that can control aspects of the stream, and it
allows each service to create their own user interface if they create
a service plugin module.
- Testing out saving of current service information. Saves/loads from
JSON in to obs_data_t, seems to be working quite nicely, and the
service object information is saved/preserved on exit, and loaded
again on startup.
- I agonized over the settings user interface for days, and eventually
I just decided that the only way that users weren't going to be
fumbling over options was to split up the settings in to simple/basic
output, pre-configured, and then advanced for advanced use (such as
multiple outputs or services, which I'll implement later).
This was particularly painful to really design right, I wanted more
features and wanted to include everything in one interface but
ultimately just realized from experience that users are just not
technically knowledgable about it and will end up fumbling with the
settings rather than getting things done.
Basically, what this means is that casual users only have to enter in
about 3 things to configure their stream: Stream key, audio bitrate,
and video bitrate. I am really happy with this interface for those
types of users, but it definitely won't be sufficient for advanced
usage or for custom outputs, so that stuff will have to be separated.
- Improved the JSON usage for the 'common streaming services' context,
I realized that JSON arrays are there to ensure sorting, while
forgetting that general items are optimized for hashing. So
basically I'm just using arrays now to sort items in it.
2014-04-24 01:49:07 -07:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
if (strcmp(type, "scene") == 0)
|
|
|
|
|
icon = GetSceneIcon();
|
|
|
|
|
else
|
|
|
|
|
icon = GetSourceIcon(type);
|
2023-03-20 08:36:03 +01:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
popupItem->setIcon(icon);
|
2023-03-20 08:36:03 +01:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
QAction *after = getActionAfter(popup, qname);
|
|
|
|
|
popup->insertAction(after, popupItem);
|
|
|
|
|
};
|
2024-01-31 15:15:57 +09:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
while (obs_enum_input_types2(idx++, &type, &unversioned_type)) {
|
|
|
|
|
const char *name = obs_source_get_display_name(type);
|
|
|
|
|
uint32_t caps = obs_get_source_output_flags(type);
|
2023-03-20 08:36:03 +01:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
if ((caps & OBS_SOURCE_CAP_DISABLED) != 0)
|
|
|
|
|
continue;
|
obs-studio UI: Implement stream settings UI
- Updated the services API so that it links up with an output and
the output gets data from that service rather than via settings.
This allows the service context to have control over how an output is
used, and makes it so that the URL/key/etc isn't necessarily some
static setting.
Also, if the service is attached to an output, it will stick around
until the output is destroyed.
- The settings interface has been updated so that it can allow the
usage of service plugins. What this means is that now you can create
a service plugin that can control aspects of the stream, and it
allows each service to create their own user interface if they create
a service plugin module.
- Testing out saving of current service information. Saves/loads from
JSON in to obs_data_t, seems to be working quite nicely, and the
service object information is saved/preserved on exit, and loaded
again on startup.
- I agonized over the settings user interface for days, and eventually
I just decided that the only way that users weren't going to be
fumbling over options was to split up the settings in to simple/basic
output, pre-configured, and then advanced for advanced use (such as
multiple outputs or services, which I'll implement later).
This was particularly painful to really design right, I wanted more
features and wanted to include everything in one interface but
ultimately just realized from experience that users are just not
technically knowledgable about it and will end up fumbling with the
settings rather than getting things done.
Basically, what this means is that casual users only have to enter in
about 3 things to configure their stream: Stream key, audio bitrate,
and video bitrate. I am really happy with this interface for those
types of users, but it definitely won't be sufficient for advanced
usage or for custom outputs, so that stuff will have to be separated.
- Improved the JSON usage for the 'common streaming services' context,
I realized that JSON arrays are there to ensure sorting, while
forgetting that general items are optimized for hashing. So
basically I'm just using arrays now to sort items in it.
2014-04-24 01:49:07 -07:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
if ((caps & OBS_SOURCE_DEPRECATED) == 0) {
|
|
|
|
|
addSource(popup, unversioned_type, name);
|
|
|
|
|
} else {
|
|
|
|
|
addSource(deprecated, unversioned_type, name);
|
|
|
|
|
foundDeprecated = true;
|
|
|
|
|
}
|
|
|
|
|
foundValues = true;
|
|
|
|
|
}
|
2015-07-11 08:04:12 +02:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
addSource(popup, "scene", Str("Basic.Scene"));
|
obs-studio UI: Implement stream settings UI
- Updated the services API so that it links up with an output and
the output gets data from that service rather than via settings.
This allows the service context to have control over how an output is
used, and makes it so that the URL/key/etc isn't necessarily some
static setting.
Also, if the service is attached to an output, it will stick around
until the output is destroyed.
- The settings interface has been updated so that it can allow the
usage of service plugins. What this means is that now you can create
a service plugin that can control aspects of the stream, and it
allows each service to create their own user interface if they create
a service plugin module.
- Testing out saving of current service information. Saves/loads from
JSON in to obs_data_t, seems to be working quite nicely, and the
service object information is saved/preserved on exit, and loaded
again on startup.
- I agonized over the settings user interface for days, and eventually
I just decided that the only way that users weren't going to be
fumbling over options was to split up the settings in to simple/basic
output, pre-configured, and then advanced for advanced use (such as
multiple outputs or services, which I'll implement later).
This was particularly painful to really design right, I wanted more
features and wanted to include everything in one interface but
ultimately just realized from experience that users are just not
technically knowledgable about it and will end up fumbling with the
settings rather than getting things done.
Basically, what this means is that casual users only have to enter in
about 3 things to configure their stream: Stream key, audio bitrate,
and video bitrate. I am really happy with this interface for those
types of users, but it definitely won't be sufficient for advanced
usage or for custom outputs, so that stuff will have to be separated.
- Improved the JSON usage for the 'common streaming services' context,
I realized that JSON arrays are there to ensure sorting, while
forgetting that general items are optimized for hashing. So
basically I'm just using arrays now to sort items in it.
2014-04-24 01:49:07 -07:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
popup->addSeparator();
|
|
|
|
|
QAction *addGroup = new QAction(QTStr("Group"), this);
|
|
|
|
|
addGroup->setIcon(GetGroupIcon());
|
|
|
|
|
connect(addGroup, &QAction::triggered, [this]() { AddSource("group"); });
|
|
|
|
|
popup->addAction(addGroup);
|
obs-studio UI: Implement stream settings UI
- Updated the services API so that it links up with an output and
the output gets data from that service rather than via settings.
This allows the service context to have control over how an output is
used, and makes it so that the URL/key/etc isn't necessarily some
static setting.
Also, if the service is attached to an output, it will stick around
until the output is destroyed.
- The settings interface has been updated so that it can allow the
usage of service plugins. What this means is that now you can create
a service plugin that can control aspects of the stream, and it
allows each service to create their own user interface if they create
a service plugin module.
- Testing out saving of current service information. Saves/loads from
JSON in to obs_data_t, seems to be working quite nicely, and the
service object information is saved/preserved on exit, and loaded
again on startup.
- I agonized over the settings user interface for days, and eventually
I just decided that the only way that users weren't going to be
fumbling over options was to split up the settings in to simple/basic
output, pre-configured, and then advanced for advanced use (such as
multiple outputs or services, which I'll implement later).
This was particularly painful to really design right, I wanted more
features and wanted to include everything in one interface but
ultimately just realized from experience that users are just not
technically knowledgable about it and will end up fumbling with the
settings rather than getting things done.
Basically, what this means is that casual users only have to enter in
about 3 things to configure their stream: Stream key, audio bitrate,
and video bitrate. I am really happy with this interface for those
types of users, but it definitely won't be sufficient for advanced
usage or for custom outputs, so that stuff will have to be separated.
- Improved the JSON usage for the 'common streaming services' context,
I realized that JSON arrays are there to ensure sorting, while
forgetting that general items are optimized for hashing. So
basically I'm just using arrays now to sort items in it.
2014-04-24 01:49:07 -07:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
if (!foundDeprecated) {
|
|
|
|
|
delete deprecated;
|
|
|
|
|
deprecated = nullptr;
|
|
|
|
|
}
|
obs-studio UI: Implement stream settings UI
- Updated the services API so that it links up with an output and
the output gets data from that service rather than via settings.
This allows the service context to have control over how an output is
used, and makes it so that the URL/key/etc isn't necessarily some
static setting.
Also, if the service is attached to an output, it will stick around
until the output is destroyed.
- The settings interface has been updated so that it can allow the
usage of service plugins. What this means is that now you can create
a service plugin that can control aspects of the stream, and it
allows each service to create their own user interface if they create
a service plugin module.
- Testing out saving of current service information. Saves/loads from
JSON in to obs_data_t, seems to be working quite nicely, and the
service object information is saved/preserved on exit, and loaded
again on startup.
- I agonized over the settings user interface for days, and eventually
I just decided that the only way that users weren't going to be
fumbling over options was to split up the settings in to simple/basic
output, pre-configured, and then advanced for advanced use (such as
multiple outputs or services, which I'll implement later).
This was particularly painful to really design right, I wanted more
features and wanted to include everything in one interface but
ultimately just realized from experience that users are just not
technically knowledgable about it and will end up fumbling with the
settings rather than getting things done.
Basically, what this means is that casual users only have to enter in
about 3 things to configure their stream: Stream key, audio bitrate,
and video bitrate. I am really happy with this interface for those
types of users, but it definitely won't be sufficient for advanced
usage or for custom outputs, so that stuff will have to be separated.
- Improved the JSON usage for the 'common streaming services' context,
I realized that JSON arrays are there to ensure sorting, while
forgetting that general items are optimized for hashing. So
basically I'm just using arrays now to sort items in it.
2014-04-24 01:49:07 -07:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
if (!foundValues) {
|
|
|
|
|
delete popup;
|
|
|
|
|
popup = nullptr;
|
2015-07-03 10:26:18 -07:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
} else if (foundDeprecated) {
|
|
|
|
|
popup->addSeparator();
|
|
|
|
|
popup->addMenu(deprecated);
|
|
|
|
|
}
|
2024-05-02 22:14:16 +02:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
return popup;
|
|
|
|
|
}
|
2019-02-06 22:10:08 -08:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
void OBSBasic::AddSourcePopupMenu(const QPoint &pos)
|
2014-03-06 21:08:12 -07:00
|
|
|
{
|
2024-12-11 17:43:35 +01:00
|
|
|
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;
|
2014-03-06 21:08:12 -07:00
|
|
|
}
|
|
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
QScopedPointer<QMenu> popup(CreateAddSourcePopupMenu());
|
|
|
|
|
if (popup)
|
|
|
|
|
popup->exec(pos);
|
|
|
|
|
}
|
2014-03-06 21:08:12 -07:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
void OBSBasic::on_actionAddSource_triggered()
|
|
|
|
|
{
|
|
|
|
|
AddSourcePopupMenu(QCursor::pos());
|
|
|
|
|
}
|
UI: Support fractional scaling for Canvas/Base size
Without this change, new profiles created on systems using a fractional
scaling factor (e.g., 125%, 150%, 175%) will get an incorrect (X, Y)
pair for the new canvas size. For a display with 125% scaling, OBS would
set (1536, 864) instead of (1920, 1080). With this change, it will set
(1920, 1080).
2020-12-29 11:17:05 -05:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
static bool remove_items(obs_scene_t *, obs_sceneitem_t *item, void *param)
|
|
|
|
|
{
|
|
|
|
|
vector<OBSSceneItem> &items = *reinterpret_cast<vector<OBSSceneItem> *>(param);
|
2017-04-28 18:02:03 -07:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
if (obs_sceneitem_selected(item)) {
|
|
|
|
|
items.emplace_back(item);
|
|
|
|
|
} else if (obs_sceneitem_is_group(item)) {
|
|
|
|
|
obs_sceneitem_group_enum_items(item, remove_items, &items);
|
2017-04-28 18:02:03 -07:00
|
|
|
}
|
2024-12-11 17:43:35 +01:00
|
|
|
return true;
|
|
|
|
|
};
|
2017-04-28 18:02:03 -07:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
void OBSBasic::on_actionRemoveSource_triggered()
|
|
|
|
|
{
|
|
|
|
|
vector<OBSSceneItem> items;
|
|
|
|
|
OBSScene scene = GetCurrentScene();
|
|
|
|
|
obs_source_t *scene_source = obs_scene_get_source(scene);
|
2018-10-04 20:06:54 -07:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
obs_scene_enum_items(scene, remove_items, &items);
|
2024-10-04 17:33:58 -04:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
if (!items.size())
|
|
|
|
|
return;
|
2018-10-04 20:06:54 -07:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
/* ------------------------------------- */
|
|
|
|
|
/* confirm action with user */
|
2015-07-03 09:49:22 -07:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
bool confirmed = false;
|
2015-07-03 09:49:22 -07:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
if (items.size() > 1) {
|
|
|
|
|
QString text = QTStr("ConfirmRemove.TextMultiple").arg(QString::number(items.size()));
|
2019-10-10 03:29:09 +02:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
QMessageBox remove_items(this);
|
|
|
|
|
remove_items.setText(text);
|
|
|
|
|
QPushButton *Yes = remove_items.addButton(QTStr("Yes"), QMessageBox::YesRole);
|
|
|
|
|
remove_items.setDefaultButton(Yes);
|
|
|
|
|
remove_items.addButton(QTStr("No"), QMessageBox::NoRole);
|
|
|
|
|
remove_items.setIcon(QMessageBox::Question);
|
|
|
|
|
remove_items.setWindowTitle(QTStr("ConfirmRemove.Title"));
|
|
|
|
|
remove_items.exec();
|
2020-11-11 09:48:39 -08:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
confirmed = Yes == remove_items.clickedButton();
|
|
|
|
|
} else {
|
|
|
|
|
OBSSceneItem &item = items[0];
|
|
|
|
|
obs_source_t *source = obs_sceneitem_get_source(item);
|
|
|
|
|
if (source && QueryRemoveSource(source))
|
|
|
|
|
confirmed = true;
|
2021-09-15 20:16:07 +02:00
|
|
|
}
|
2024-12-11 17:43:35 +01:00
|
|
|
if (!confirmed)
|
|
|
|
|
return;
|
2021-09-15 20:16:07 +02:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
/* ----------------------------------------------- */
|
|
|
|
|
/* save undo data */
|
2023-04-03 15:48:55 +02:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
OBSData undo_data = BackupScene(scene_source);
|
2023-03-24 12:44:37 +01:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
/* ----------------------------------------------- */
|
|
|
|
|
/* remove items */
|
2024-01-10 10:05:56 +01:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
for (auto &item : items)
|
|
|
|
|
obs_sceneitem_remove(item);
|
2024-01-10 10:05:56 +01:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
/* ----------------------------------------------- */
|
|
|
|
|
/* save redo data */
|
2024-01-10 10:05:56 +01:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
OBSData redo_data = BackupScene(scene_source);
|
2015-07-03 09:49:22 -07:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
/* ----------------------------------------------- */
|
|
|
|
|
/* add undo/redo action */
|
2018-10-04 20:06:54 -07:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
QString action_name;
|
|
|
|
|
if (items.size() > 1) {
|
|
|
|
|
action_name = QTStr("Undo.Sources.Multi").arg(QString::number(items.size()));
|
|
|
|
|
} else {
|
|
|
|
|
QString str = QTStr("Undo.Delete");
|
|
|
|
|
action_name = str.arg(obs_source_get_name(obs_sceneitem_get_source(items[0])));
|
2017-04-28 18:00:11 -07:00
|
|
|
}
|
|
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
CreateSceneUndoRedoAction(action_name, undo_data, redo_data);
|
|
|
|
|
}
|
2015-07-03 10:26:18 -07:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
void OBSBasic::on_actionInteract_triggered()
|
|
|
|
|
{
|
|
|
|
|
OBSSceneItem item = GetCurrentSceneItem();
|
|
|
|
|
OBSSource source = obs_sceneitem_get_source(item);
|
2015-07-03 10:26:18 -07:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
if (source)
|
|
|
|
|
CreateInteractionWindow(source);
|
|
|
|
|
}
|
2014-03-06 21:08:12 -07:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
void OBSBasic::on_actionSourceProperties_triggered()
|
|
|
|
|
{
|
|
|
|
|
OBSSceneItem item = GetCurrentSceneItem();
|
|
|
|
|
OBSSource source = obs_sceneitem_get_source(item);
|
2024-09-03 16:29:30 +02:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
if (source)
|
|
|
|
|
CreatePropertiesWindow(source);
|
|
|
|
|
}
|
2019-02-06 22:10:08 -08:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
void OBSBasic::on_actionSourceUp_triggered()
|
|
|
|
|
{
|
|
|
|
|
MoveSceneItem(OBS_ORDER_MOVE_UP, QTStr("Undo.MoveUp"));
|
2014-03-06 21:08:12 -07:00
|
|
|
}
|
|
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
void OBSBasic::on_actionSourceDown_triggered()
|
|
|
|
|
{
|
|
|
|
|
MoveSceneItem(OBS_ORDER_MOVE_DOWN, QTStr("Undo.MoveDown"));
|
|
|
|
|
}
|
2019-02-05 18:05:19 -08:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
void OBSBasic::on_actionMoveUp_triggered()
|
2019-02-05 18:05:19 -08:00
|
|
|
{
|
2024-12-11 17:43:35 +01:00
|
|
|
MoveSceneItem(OBS_ORDER_MOVE_UP, QTStr("Undo.MoveUp"));
|
2019-02-05 18:05:19 -08:00
|
|
|
}
|
|
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
void OBSBasic::on_actionMoveDown_triggered()
|
2014-03-06 21:08:12 -07:00
|
|
|
{
|
2024-12-11 17:43:35 +01:00
|
|
|
MoveSceneItem(OBS_ORDER_MOVE_DOWN, QTStr("Undo.MoveDown"));
|
|
|
|
|
}
|
2015-07-11 08:04:12 +02:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
void OBSBasic::on_actionMoveToTop_triggered()
|
|
|
|
|
{
|
|
|
|
|
MoveSceneItem(OBS_ORDER_MOVE_TOP, QTStr("Undo.MoveToTop"));
|
|
|
|
|
}
|
2015-06-23 19:38:01 -07:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
void OBSBasic::on_actionMoveToBottom_triggered()
|
|
|
|
|
{
|
|
|
|
|
MoveSceneItem(OBS_ORDER_MOVE_BOTTOM, QTStr("Undo.MoveToBottom"));
|
|
|
|
|
}
|
2014-03-06 21:08:12 -07:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
void OBSBasic::OpenFilters(OBSSource source)
|
|
|
|
|
{
|
|
|
|
|
if (source == nullptr) {
|
|
|
|
|
OBSSceneItem item = GetCurrentSceneItem();
|
|
|
|
|
source = obs_sceneitem_get_source(item);
|
2015-06-23 19:38:01 -07:00
|
|
|
}
|
2024-12-11 17:43:35 +01:00
|
|
|
CreateFiltersWindow(source);
|
2014-03-06 21:08:12 -07:00
|
|
|
}
|
|
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
void OBSBasic::OpenProperties(OBSSource source)
|
2014-05-03 22:54:38 -07:00
|
|
|
{
|
2024-12-11 17:43:35 +01:00
|
|
|
if (source == nullptr) {
|
|
|
|
|
OBSSceneItem item = GetCurrentSceneItem();
|
|
|
|
|
source = obs_sceneitem_get_source(item);
|
|
|
|
|
}
|
|
|
|
|
CreatePropertiesWindow(source);
|
2014-05-03 22:54:38 -07:00
|
|
|
}
|
|
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
void OBSBasic::OpenInteraction(OBSSource source)
|
UI: Add scene editing
So, scene editing was interesting (and by interesting I mean
excruciating). I almost implemented 'manipulator' visuals (ala 3dsmax
for example), and used 3 modes for controlling position/rotation/size,
but in a 2D editing, it felt clunky, so I defaulted back to simply
click-and-drag for movement, and then took a similar though slightly
different looking approach for handling scaling and reszing.
I also added a number of menu item helpers related to positioning,
scaling, rotating, flipping, and resetting the transform back to
default.
There is also a new 'transform' dialog (accessible via menu) which will
allow you to manually edit every single transform variable of a scene
item directly if desired.
If a scene item does not have bounds active, pulling on the sides of a
source will cause it to resize it via base scale rather than by the
bounding box system (if the source resizes that scale will apply). If
bounds are active, it will modify the bounding box only instead.
How a source scales when a bounding box is active depends on the type of
bounds being used. You can set it to scale to the inner bounds, the
outer bounds, scale to bounds width only, scale to bounds height only,
and a setting to stretch to bounds (which forces a source to always draw
at the bounding box size rather than be affected by its internal size).
You can also set it to be used as a 'maximum' size, so that the source
doesn't necessarily get scaled unless it extends beyond the bounds.
Like in OBS1, objects will snap to the edges unless the control key is
pressed. However, this will now happen even if the object is rotated or
oriented in any strange way. Snapping will also occur when stretching
or changing the bounding box size.
2014-06-15 00:54:48 -07:00
|
|
|
{
|
2024-12-11 17:43:35 +01:00
|
|
|
if (source == nullptr) {
|
|
|
|
|
OBSSceneItem item = GetCurrentSceneItem();
|
|
|
|
|
source = obs_sceneitem_get_source(item);
|
UI: Add scene editing
So, scene editing was interesting (and by interesting I mean
excruciating). I almost implemented 'manipulator' visuals (ala 3dsmax
for example), and used 3 modes for controlling position/rotation/size,
but in a 2D editing, it felt clunky, so I defaulted back to simply
click-and-drag for movement, and then took a similar though slightly
different looking approach for handling scaling and reszing.
I also added a number of menu item helpers related to positioning,
scaling, rotating, flipping, and resetting the transform back to
default.
There is also a new 'transform' dialog (accessible via menu) which will
allow you to manually edit every single transform variable of a scene
item directly if desired.
If a scene item does not have bounds active, pulling on the sides of a
source will cause it to resize it via base scale rather than by the
bounding box system (if the source resizes that scale will apply). If
bounds are active, it will modify the bounding box only instead.
How a source scales when a bounding box is active depends on the type of
bounds being used. You can set it to scale to the inner bounds, the
outer bounds, scale to bounds width only, scale to bounds height only,
and a setting to stretch to bounds (which forces a source to always draw
at the bounding box size rather than be affected by its internal size).
You can also set it to be used as a 'maximum' size, so that the source
doesn't necessarily get scaled unless it extends beyond the bounds.
Like in OBS1, objects will snap to the edges unless the control key is
pressed. However, this will now happen even if the object is rotated or
oriented in any strange way. Snapping will also occur when stretching
or changing the bounding box size.
2014-06-15 00:54:48 -07:00
|
|
|
}
|
2024-12-11 17:43:35 +01:00
|
|
|
CreateInteractionWindow(source);
|
UI: Add scene editing
So, scene editing was interesting (and by interesting I mean
excruciating). I almost implemented 'manipulator' visuals (ala 3dsmax
for example), and used 3 modes for controlling position/rotation/size,
but in a 2D editing, it felt clunky, so I defaulted back to simply
click-and-drag for movement, and then took a similar though slightly
different looking approach for handling scaling and reszing.
I also added a number of menu item helpers related to positioning,
scaling, rotating, flipping, and resetting the transform back to
default.
There is also a new 'transform' dialog (accessible via menu) which will
allow you to manually edit every single transform variable of a scene
item directly if desired.
If a scene item does not have bounds active, pulling on the sides of a
source will cause it to resize it via base scale rather than by the
bounding box system (if the source resizes that scale will apply). If
bounds are active, it will modify the bounding box only instead.
How a source scales when a bounding box is active depends on the type of
bounds being used. You can set it to scale to the inner bounds, the
outer bounds, scale to bounds width only, scale to bounds height only,
and a setting to stretch to bounds (which forces a source to always draw
at the bounding box size rather than be affected by its internal size).
You can also set it to be used as a 'maximum' size, so that the source
doesn't necessarily get scaled unless it extends beyond the bounds.
Like in OBS1, objects will snap to the edges unless the control key is
pressed. However, this will now happen even if the object is rotated or
oriented in any strange way. Snapping will also occur when stretching
or changing the bounding box size.
2014-06-15 00:54:48 -07:00
|
|
|
}
|
|
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
void OBSBasic::OpenEditTransform(OBSSceneItem item)
|
2016-12-09 14:40:04 -08:00
|
|
|
{
|
2024-12-11 17:43:35 +01:00
|
|
|
if (!item)
|
|
|
|
|
item = GetCurrentSceneItem();
|
|
|
|
|
if (!item)
|
|
|
|
|
return;
|
|
|
|
|
CreateEditTransformWindow(item);
|
|
|
|
|
}
|
2016-12-09 14:40:04 -08:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
int OBSBasic::GetTopSelectedSourceItem()
|
2015-02-06 03:17:33 -08:00
|
|
|
{
|
2024-12-11 17:43:35 +01:00
|
|
|
QModelIndexList selectedItems = ui->sources->selectionModel()->selectedIndexes();
|
|
|
|
|
return selectedItems.count() ? selectedItems[0].row() : -1;
|
|
|
|
|
}
|
2024-04-03 17:08:14 +02:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
QModelIndexList OBSBasic::GetAllSelectedSourceItems()
|
|
|
|
|
{
|
|
|
|
|
return ui->sources->selectionModel()->selectedIndexes();
|
2015-02-06 03:17:33 -08:00
|
|
|
}
|
|
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
void OBSBasic::on_actionEditTransform_triggered()
|
|
|
|
|
{
|
|
|
|
|
const auto item = GetCurrentSceneItem();
|
|
|
|
|
if (!item)
|
|
|
|
|
return;
|
|
|
|
|
CreateEditTransformWindow(item);
|
|
|
|
|
}
|
2015-07-05 23:42:46 -07:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
void undo_redo(const std::string &data)
|
|
|
|
|
{
|
|
|
|
|
OBSDataAutoRelease dat = obs_data_create_from_json(data.c_str());
|
|
|
|
|
OBSSourceAutoRelease source = obs_get_source_by_uuid(obs_data_get_string(dat, "scene_uuid"));
|
2025-01-25 20:21:42 -06:00
|
|
|
OBSBasic::Get()->SetCurrentScene(source.Get(), true);
|
2016-12-21 19:17:39 -08:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
obs_scene_load_transform_states(data.c_str());
|
|
|
|
|
}
|
2016-12-21 19:17:39 -08:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
static void GetItemBox(obs_sceneitem_t *item, vec3 &tl, vec3 &br)
|
2024-04-03 17:49:15 +02:00
|
|
|
{
|
2024-12-11 17:43:35 +01:00
|
|
|
matrix4 boxTransform;
|
|
|
|
|
obs_sceneitem_get_box_transform(item, &boxTransform);
|
2024-04-03 17:49:15 +02:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
vec3_set(&tl, M_INFINITE, M_INFINITE, 0.0f);
|
|
|
|
|
vec3_set(&br, -M_INFINITE, -M_INFINITE, 0.0f);
|
2024-04-03 17:49:15 +02:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
auto GetMinPos = [&](float x, float y) {
|
|
|
|
|
vec3 pos;
|
|
|
|
|
vec3_set(&pos, x, y, 0.0f);
|
|
|
|
|
vec3_transform(&pos, &pos, &boxTransform);
|
|
|
|
|
vec3_min(&tl, &tl, &pos);
|
|
|
|
|
vec3_max(&br, &br, &pos);
|
2024-04-03 17:49:15 +02:00
|
|
|
};
|
|
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
GetMinPos(0.0f, 0.0f);
|
|
|
|
|
GetMinPos(1.0f, 0.0f);
|
|
|
|
|
GetMinPos(0.0f, 1.0f);
|
|
|
|
|
GetMinPos(1.0f, 1.0f);
|
2024-04-03 17:49:15 +02:00
|
|
|
}
|
|
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
static vec3 GetItemTL(obs_sceneitem_t *item)
|
2014-02-02 15:23:38 -07:00
|
|
|
{
|
2024-12-11 17:43:35 +01:00
|
|
|
vec3 tl, br;
|
|
|
|
|
GetItemBox(item, tl, br);
|
|
|
|
|
return tl;
|
|
|
|
|
}
|
2014-02-02 15:23:38 -07:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
static void SetItemTL(obs_sceneitem_t *item, const vec3 &tl)
|
|
|
|
|
{
|
|
|
|
|
vec3 newTL;
|
|
|
|
|
vec2 pos;
|
2024-09-03 16:29:58 +02:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
obs_sceneitem_get_pos(item, &pos);
|
|
|
|
|
newTL = GetItemTL(item);
|
|
|
|
|
pos.x += tl.x - newTL.x;
|
|
|
|
|
pos.y += tl.y - newTL.y;
|
|
|
|
|
obs_sceneitem_set_pos(item, &pos);
|
|
|
|
|
}
|
2014-07-20 17:40:57 -07:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
static bool RotateSelectedSources(obs_scene_t * /* scene */, obs_sceneitem_t *item, void *param)
|
2021-11-03 21:11:06 +01:00
|
|
|
{
|
2024-12-11 17:43:35 +01:00
|
|
|
if (obs_sceneitem_is_group(item))
|
|
|
|
|
obs_sceneitem_group_enum_items(item, RotateSelectedSources, param);
|
|
|
|
|
if (!obs_sceneitem_selected(item))
|
|
|
|
|
return true;
|
|
|
|
|
if (obs_sceneitem_locked(item))
|
|
|
|
|
return true;
|
2021-11-03 21:11:06 +01:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
float rot = *reinterpret_cast<float *>(param);
|
2019-04-07 03:34:21 -04:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
vec3 tl = GetItemTL(item);
|
2019-04-23 15:56:30 +08:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
rot += obs_sceneitem_get_rot(item);
|
|
|
|
|
if (rot >= 360.0f)
|
|
|
|
|
rot -= 360.0f;
|
|
|
|
|
else if (rot <= -360.0f)
|
|
|
|
|
rot += 360.0f;
|
|
|
|
|
obs_sceneitem_set_rot(item, rot);
|
2019-04-23 15:56:30 +08:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
obs_sceneitem_force_update_transform(item);
|
2019-05-05 20:00:06 -07:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
SetItemTL(item, tl);
|
2019-05-05 20:00:06 -07:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
return true;
|
|
|
|
|
};
|
2019-07-07 15:47:29 -07:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
void OBSBasic::on_actionRotate90CW_triggered()
|
2019-07-07 15:47:29 -07:00
|
|
|
{
|
2024-12-11 17:43:35 +01:00
|
|
|
float f90CW = 90.0f;
|
|
|
|
|
OBSDataAutoRelease wrapper = obs_scene_save_transform_states(GetCurrentScene(), false);
|
|
|
|
|
obs_scene_enum_items(GetCurrentScene(), RotateSelectedSources, &f90CW);
|
|
|
|
|
OBSDataAutoRelease rwrapper = obs_scene_save_transform_states(GetCurrentScene(), false);
|
2019-07-07 15:47:29 -07:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
std::string undo_data(obs_data_get_json(wrapper));
|
|
|
|
|
std::string redo_data(obs_data_get_json(rwrapper));
|
|
|
|
|
undo_s.add_action(
|
|
|
|
|
QTStr("Undo.Transform.Rotate").arg(obs_source_get_name(obs_scene_get_source(GetCurrentScene()))),
|
|
|
|
|
undo_redo, undo_redo, undo_data, redo_data);
|
2019-07-07 15:47:29 -07:00
|
|
|
}
|
|
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
void OBSBasic::on_actionRotate90CCW_triggered()
|
2019-07-07 15:47:29 -07:00
|
|
|
{
|
2024-12-11 17:43:35 +01:00
|
|
|
float f90CCW = -90.0f;
|
|
|
|
|
OBSDataAutoRelease wrapper = obs_scene_save_transform_states(GetCurrentScene(), false);
|
|
|
|
|
obs_scene_enum_items(GetCurrentScene(), RotateSelectedSources, &f90CCW);
|
|
|
|
|
OBSDataAutoRelease rwrapper = obs_scene_save_transform_states(GetCurrentScene(), false);
|
2019-10-02 19:44:47 -04:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
std::string undo_data(obs_data_get_json(wrapper));
|
|
|
|
|
std::string redo_data(obs_data_get_json(rwrapper));
|
|
|
|
|
undo_s.add_action(
|
|
|
|
|
QTStr("Undo.Transform.Rotate").arg(obs_source_get_name(obs_scene_get_source(GetCurrentScene()))),
|
|
|
|
|
undo_redo, undo_redo, undo_data, redo_data);
|
2019-07-07 15:47:29 -07:00
|
|
|
}
|
|
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
void OBSBasic::on_actionRotate180_triggered()
|
2019-07-07 15:47:29 -07:00
|
|
|
{
|
2024-12-11 17:43:35 +01:00
|
|
|
float f180 = 180.0f;
|
|
|
|
|
OBSDataAutoRelease wrapper = obs_scene_save_transform_states(GetCurrentScene(), false);
|
|
|
|
|
obs_scene_enum_items(GetCurrentScene(), RotateSelectedSources, &f180);
|
|
|
|
|
OBSDataAutoRelease rwrapper = obs_scene_save_transform_states(GetCurrentScene(), false);
|
2019-07-07 15:47:29 -07:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
std::string undo_data(obs_data_get_json(wrapper));
|
|
|
|
|
std::string redo_data(obs_data_get_json(rwrapper));
|
|
|
|
|
undo_s.add_action(
|
|
|
|
|
QTStr("Undo.Transform.Rotate").arg(obs_source_get_name(obs_scene_get_source(GetCurrentScene()))),
|
|
|
|
|
undo_redo, undo_redo, undo_data, redo_data);
|
2019-07-07 15:47:29 -07:00
|
|
|
}
|
|
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
static bool MultiplySelectedItemScale(obs_scene_t * /* scene */, obs_sceneitem_t *item, void *param)
|
2019-07-07 15:47:29 -07:00
|
|
|
{
|
2024-12-11 17:43:35 +01:00
|
|
|
vec2 &mul = *reinterpret_cast<vec2 *>(param);
|
2019-06-12 04:56:00 -05:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
if (obs_sceneitem_is_group(item))
|
|
|
|
|
obs_sceneitem_group_enum_items(item, MultiplySelectedItemScale, param);
|
|
|
|
|
if (!obs_sceneitem_selected(item))
|
|
|
|
|
return true;
|
|
|
|
|
if (obs_sceneitem_locked(item))
|
|
|
|
|
return true;
|
2019-09-27 15:04:07 +02:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
vec3 tl = GetItemTL(item);
|
2019-09-27 15:04:07 +02:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
vec2 scale;
|
|
|
|
|
obs_sceneitem_get_scale(item, &scale);
|
|
|
|
|
vec2_mul(&scale, &scale, &mul);
|
|
|
|
|
obs_sceneitem_set_scale(item, &scale);
|
2019-09-27 15:04:07 +02:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
obs_sceneitem_force_update_transform(item);
|
2019-09-27 15:04:07 +02:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
SetItemTL(item, tl);
|
2019-12-27 17:12:31 +08:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
return true;
|
2019-12-27 17:12:31 +08:00
|
|
|
}
|
|
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
void OBSBasic::on_actionFlipHorizontal_triggered()
|
2019-12-27 17:12:31 +08:00
|
|
|
{
|
2024-12-11 17:43:35 +01:00
|
|
|
vec2 scale;
|
|
|
|
|
vec2_set(&scale, -1.0f, 1.0f);
|
|
|
|
|
OBSDataAutoRelease wrapper = obs_scene_save_transform_states(GetCurrentScene(), false);
|
|
|
|
|
obs_scene_enum_items(GetCurrentScene(), MultiplySelectedItemScale, &scale);
|
|
|
|
|
OBSDataAutoRelease rwrapper = obs_scene_save_transform_states(GetCurrentScene(), false);
|
2020-09-08 20:29:40 -07:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
std::string undo_data(obs_data_get_json(wrapper));
|
|
|
|
|
std::string redo_data(obs_data_get_json(rwrapper));
|
|
|
|
|
undo_s.add_action(
|
|
|
|
|
QTStr("Undo.Transform.HFlip").arg(obs_source_get_name(obs_scene_get_source(GetCurrentScene()))),
|
|
|
|
|
undo_redo, undo_redo, undo_data, redo_data);
|
2024-03-28 16:43:12 -04:00
|
|
|
}
|
|
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
void OBSBasic::on_actionFlipVertical_triggered()
|
2024-03-28 16:43:12 -04:00
|
|
|
{
|
2024-12-11 17:43:35 +01:00
|
|
|
vec2 scale;
|
|
|
|
|
vec2_set(&scale, 1.0f, -1.0f);
|
|
|
|
|
OBSDataAutoRelease wrapper = obs_scene_save_transform_states(GetCurrentScene(), false);
|
|
|
|
|
obs_scene_enum_items(GetCurrentScene(), MultiplySelectedItemScale, &scale);
|
|
|
|
|
OBSDataAutoRelease rwrapper = obs_scene_save_transform_states(GetCurrentScene(), false);
|
2024-03-28 16:43:12 -04:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
std::string undo_data(obs_data_get_json(wrapper));
|
|
|
|
|
std::string redo_data(obs_data_get_json(rwrapper));
|
|
|
|
|
undo_s.add_action(
|
|
|
|
|
QTStr("Undo.Transform.VFlip").arg(obs_source_get_name(obs_scene_get_source(GetCurrentScene()))),
|
|
|
|
|
undo_redo, undo_redo, undo_data, redo_data);
|
2019-12-27 17:12:31 +08:00
|
|
|
}
|
|
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
static bool CenterAlignSelectedItems(obs_scene_t * /* scene */, obs_sceneitem_t *item, void *param)
|
2019-06-12 04:56:00 -05:00
|
|
|
{
|
2024-12-11 17:43:35 +01:00
|
|
|
obs_bounds_type boundsType = *reinterpret_cast<obs_bounds_type *>(param);
|
2019-06-12 04:56:00 -05:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
if (obs_sceneitem_is_group(item))
|
|
|
|
|
obs_sceneitem_group_enum_items(item, CenterAlignSelectedItems, param);
|
|
|
|
|
if (!obs_sceneitem_selected(item))
|
|
|
|
|
return true;
|
|
|
|
|
if (obs_sceneitem_locked(item))
|
|
|
|
|
return true;
|
2019-06-12 04:56:00 -05:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
obs_video_info ovi;
|
|
|
|
|
obs_get_video_info(&ovi);
|
2019-09-27 15:04:07 +02:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
obs_transform_info itemInfo;
|
|
|
|
|
vec2_set(&itemInfo.pos, 0.0f, 0.0f);
|
|
|
|
|
vec2_set(&itemInfo.scale, 1.0f, 1.0f);
|
|
|
|
|
itemInfo.alignment = OBS_ALIGN_LEFT | OBS_ALIGN_TOP;
|
|
|
|
|
itemInfo.rot = 0.0f;
|
|
|
|
|
|
|
|
|
|
vec2_set(&itemInfo.bounds, float(ovi.base_width), float(ovi.base_height));
|
|
|
|
|
itemInfo.bounds_type = boundsType;
|
|
|
|
|
itemInfo.bounds_alignment = OBS_ALIGN_CENTER;
|
|
|
|
|
itemInfo.crop_to_bounds = obs_sceneitem_get_bounds_crop(item);
|
2019-06-12 04:56:00 -05:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
obs_sceneitem_set_info2(item, &itemInfo);
|
2019-06-12 04:56:00 -05:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
return true;
|
2019-06-12 04:56:00 -05:00
|
|
|
}
|
|
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
void OBSBasic::on_actionFitToScreen_triggered()
|
2019-06-12 04:56:00 -05:00
|
|
|
{
|
2024-12-11 17:43:35 +01:00
|
|
|
obs_bounds_type boundsType = OBS_BOUNDS_SCALE_INNER;
|
|
|
|
|
OBSDataAutoRelease wrapper = obs_scene_save_transform_states(GetCurrentScene(), false);
|
|
|
|
|
obs_scene_enum_items(GetCurrentScene(), CenterAlignSelectedItems, &boundsType);
|
|
|
|
|
OBSDataAutoRelease rwrapper = obs_scene_save_transform_states(GetCurrentScene(), false);
|
2019-06-12 04:56:00 -05:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
std::string undo_data(obs_data_get_json(wrapper));
|
|
|
|
|
std::string redo_data(obs_data_get_json(rwrapper));
|
|
|
|
|
undo_s.add_action(
|
|
|
|
|
QTStr("Undo.Transform.FitToScreen").arg(obs_source_get_name(obs_scene_get_source(GetCurrentScene()))),
|
|
|
|
|
undo_redo, undo_redo, undo_data, redo_data);
|
2019-06-12 04:56:00 -05:00
|
|
|
}
|
2019-10-10 20:12:20 -05:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
void OBSBasic::on_actionStretchToScreen_triggered()
|
2019-10-15 23:20:35 -05:00
|
|
|
{
|
2024-12-11 17:43:35 +01:00
|
|
|
obs_bounds_type boundsType = OBS_BOUNDS_STRETCH;
|
|
|
|
|
OBSDataAutoRelease wrapper = obs_scene_save_transform_states(GetCurrentScene(), false);
|
|
|
|
|
obs_scene_enum_items(GetCurrentScene(), CenterAlignSelectedItems, &boundsType);
|
|
|
|
|
OBSDataAutoRelease rwrapper = obs_scene_save_transform_states(GetCurrentScene(), false);
|
2019-10-15 23:20:35 -05:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
std::string undo_data(obs_data_get_json(wrapper));
|
|
|
|
|
std::string redo_data(obs_data_get_json(rwrapper));
|
|
|
|
|
undo_s.add_action(QTStr("Undo.Transform.StretchToScreen")
|
|
|
|
|
.arg(obs_source_get_name(obs_scene_get_source(GetCurrentScene()))),
|
|
|
|
|
undo_redo, undo_redo, undo_data, redo_data);
|
2019-10-15 23:20:35 -05:00
|
|
|
}
|
2020-02-14 23:30:34 +11:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
void OBSBasic::CenterSelectedSceneItems(const CenterType ¢erType)
|
2020-02-14 23:30:34 +11:00
|
|
|
{
|
2024-12-11 17:43:35 +01:00
|
|
|
QModelIndexList selectedItems = GetAllSelectedSourceItems();
|
2022-02-22 00:40:17 +11:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
if (!selectedItems.count())
|
|
|
|
|
return;
|
2020-06-13 21:00:55 -05:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
vector<OBSSceneItem> items;
|
2020-06-13 21:00:55 -05:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
// Filter out items that have no size
|
|
|
|
|
for (int x = 0; x < selectedItems.count(); x++) {
|
|
|
|
|
OBSSceneItem item = ui->sources->Get(selectedItems[x].row());
|
|
|
|
|
obs_transform_info oti;
|
|
|
|
|
obs_sceneitem_get_info2(item, &oti);
|
2018-08-23 20:43:44 -05:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
obs_source_t *source = obs_sceneitem_get_source(item);
|
|
|
|
|
float width = float(obs_source_get_width(source)) * oti.scale.x;
|
|
|
|
|
float height = float(obs_source_get_height(source)) * oti.scale.y;
|
2020-08-23 05:12:46 -05:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
if (width == 0.0f || height == 0.0f)
|
|
|
|
|
continue;
|
2018-08-23 20:43:44 -05:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
items.emplace_back(item);
|
|
|
|
|
}
|
2021-01-11 23:26:02 -06:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
if (!items.size())
|
|
|
|
|
return;
|
2022-09-09 07:20:48 -05:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
// Get center x, y coordinates of items
|
|
|
|
|
vec3 center;
|
2022-09-09 07:20:48 -05:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
float top = M_INFINITE;
|
|
|
|
|
float left = M_INFINITE;
|
|
|
|
|
float right = 0.0f;
|
|
|
|
|
float bottom = 0.0f;
|
2021-02-04 06:01:35 -06:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
for (auto &item : items) {
|
|
|
|
|
vec3 tl, br;
|
2021-08-19 23:56:49 -05:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
GetItemBox(item, tl, br);
|
2020-06-27 02:31:08 +02:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
left = std::min(tl.x, left);
|
|
|
|
|
top = std::min(tl.y, top);
|
|
|
|
|
right = std::max(br.x, right);
|
|
|
|
|
bottom = std::max(br.y, bottom);
|
|
|
|
|
}
|
2023-02-10 13:27:08 +01:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
center.x = (right + left) / 2.0f;
|
|
|
|
|
center.y = (top + bottom) / 2.0f;
|
|
|
|
|
center.z = 0.0f;
|
2023-02-10 13:27:08 +01:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
// Get coordinates of screen center
|
|
|
|
|
obs_video_info ovi;
|
|
|
|
|
obs_get_video_info(&ovi);
|
2020-06-27 02:31:08 +02:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
vec3 screenCenter;
|
|
|
|
|
vec3_set(&screenCenter, float(ovi.base_width), float(ovi.base_height), 0.0f);
|
2020-06-27 02:31:08 +02:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
vec3_mulf(&screenCenter, &screenCenter, 0.5f);
|
2020-06-27 02:31:08 +02:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
// Calculate difference between screen center and item center
|
|
|
|
|
vec3 offset;
|
|
|
|
|
vec3_sub(&offset, &screenCenter, ¢er);
|
2020-06-27 02:31:08 +02:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
// Shift items by offset
|
|
|
|
|
for (auto &item : items) {
|
|
|
|
|
vec3 tl, br;
|
2022-01-27 02:37:32 +01:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
GetItemBox(item, tl, br);
|
2021-07-03 17:55:47 +12:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
vec3_add(&tl, &tl, &offset);
|
2021-07-03 17:55:47 +12:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
vec3 itemTL = GetItemTL(item);
|
2021-07-03 17:55:47 +12:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
if (centerType == CenterType::Vertical)
|
|
|
|
|
tl.x = itemTL.x;
|
|
|
|
|
else if (centerType == CenterType::Horizontal)
|
|
|
|
|
tl.y = itemTL.y;
|
2021-07-03 17:55:47 +12:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
SetItemTL(item, tl);
|
2021-07-03 17:55:47 +12:00
|
|
|
}
|
|
|
|
|
}
|
2021-11-01 02:16:56 -05:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
void OBSBasic::on_actionCenterToScreen_triggered()
|
2021-11-01 02:16:56 -05:00
|
|
|
{
|
2024-12-11 17:43:35 +01:00
|
|
|
CenterType centerType = CenterType::Scene;
|
|
|
|
|
OBSDataAutoRelease wrapper = obs_scene_save_transform_states(GetCurrentScene(), false);
|
|
|
|
|
CenterSelectedSceneItems(centerType);
|
|
|
|
|
OBSDataAutoRelease rwrapper = obs_scene_save_transform_states(GetCurrentScene(), false);
|
2022-07-29 20:31:13 +02:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
std::string undo_data(obs_data_get_json(wrapper));
|
|
|
|
|
std::string redo_data(obs_data_get_json(rwrapper));
|
|
|
|
|
undo_s.add_action(
|
|
|
|
|
QTStr("Undo.Transform.Center").arg(obs_source_get_name(obs_scene_get_source(GetCurrentScene()))),
|
|
|
|
|
undo_redo, undo_redo, undo_data, redo_data);
|
2022-07-29 20:31:13 +02:00
|
|
|
}
|
2024-06-18 14:18:22 -05:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
void OBSBasic::on_actionVerticalCenter_triggered()
|
2024-06-18 14:18:22 -05:00
|
|
|
{
|
2024-12-11 17:43:35 +01:00
|
|
|
CenterType centerType = CenterType::Vertical;
|
|
|
|
|
OBSDataAutoRelease wrapper = obs_scene_save_transform_states(GetCurrentScene(), false);
|
|
|
|
|
CenterSelectedSceneItems(centerType);
|
|
|
|
|
OBSDataAutoRelease rwrapper = obs_scene_save_transform_states(GetCurrentScene(), false);
|
|
|
|
|
|
|
|
|
|
std::string undo_data(obs_data_get_json(wrapper));
|
|
|
|
|
std::string redo_data(obs_data_get_json(rwrapper));
|
|
|
|
|
undo_s.add_action(
|
|
|
|
|
QTStr("Undo.Transform.VCenter").arg(obs_source_get_name(obs_scene_get_source(GetCurrentScene()))),
|
|
|
|
|
undo_redo, undo_redo, undo_data, redo_data);
|
2024-06-18 14:18:22 -05:00
|
|
|
}
|
2024-07-31 10:53:59 -04:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
void OBSBasic::on_actionHorizontalCenter_triggered()
|
2024-07-31 10:53:59 -04:00
|
|
|
{
|
2024-12-11 17:43:35 +01:00
|
|
|
CenterType centerType = CenterType::Horizontal;
|
|
|
|
|
OBSDataAutoRelease wrapper = obs_scene_save_transform_states(GetCurrentScene(), false);
|
|
|
|
|
CenterSelectedSceneItems(centerType);
|
|
|
|
|
OBSDataAutoRelease rwrapper = obs_scene_save_transform_states(GetCurrentScene(), false);
|
|
|
|
|
|
|
|
|
|
std::string undo_data(obs_data_get_json(wrapper));
|
|
|
|
|
std::string redo_data(obs_data_get_json(rwrapper));
|
|
|
|
|
undo_s.add_action(
|
|
|
|
|
QTStr("Undo.Transform.HCenter").arg(obs_source_get_name(obs_scene_get_source(GetCurrentScene()))),
|
|
|
|
|
undo_redo, undo_redo, undo_data, redo_data);
|
2024-07-31 10:53:59 -04:00
|
|
|
}
|
|
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
void OBSBasic::on_toggleSourceIcons_toggled(bool visible)
|
2024-07-31 10:53:59 -04:00
|
|
|
{
|
2024-12-11 17:43:35 +01:00
|
|
|
ui->sources->SetIconsVisible(visible);
|
|
|
|
|
if (advAudioWindow != nullptr)
|
|
|
|
|
advAudioWindow->SetIconsVisible(visible);
|
|
|
|
|
|
|
|
|
|
config_set_bool(App()->GetUserConfig(), "BasicWindow", "ShowSourceIcons", visible);
|
2024-07-31 10:53:59 -04:00
|
|
|
}
|
|
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
void OBSBasic::on_sourcePropertiesButton_clicked()
|
2024-07-31 10:53:59 -04:00
|
|
|
{
|
2024-12-11 17:43:35 +01:00
|
|
|
on_actionSourceProperties_triggered();
|
2024-07-31 10:53:59 -04:00
|
|
|
}
|
|
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
void OBSBasic::on_sourceFiltersButton_clicked()
|
2024-07-31 10:53:59 -04:00
|
|
|
{
|
2024-12-11 17:43:35 +01:00
|
|
|
OpenFilters();
|
2024-07-31 10:53:59 -04:00
|
|
|
}
|
2024-09-03 16:29:30 +02:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
void OBSBasic::on_sourceInteractButton_clicked()
|
2024-09-03 16:29:30 +02:00
|
|
|
{
|
2024-12-11 17:43:35 +01:00
|
|
|
on_actionInteract_triggered();
|
2024-09-03 16:29:30 +02:00
|
|
|
}
|