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/>.
|
|
|
|
|
******************************************************************************/
|
2013-11-22 16:20:52 -07:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
#include "OBSBasic.hpp"
|
|
|
|
|
#include "OBSProjector.hpp"
|
2018-07-27 21:35:08 -07:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
#include <dialogs/NameDialog.hpp>
|
2019-02-06 22:44:28 -08:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
#include <qt-wrappers.hpp>
|
2019-02-06 22:10:08 -08:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
#include <QLineEdit>
|
|
|
|
|
#include <QWidgetAction>
|
2019-02-06 22:10:08 -08:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
#include <vector>
|
2024-10-10 14:02:54 +02:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
using namespace std;
|
2019-02-06 21:38:34 -08:00
|
|
|
|
2015-12-05 05:53:38 -08:00
|
|
|
namespace {
|
|
|
|
|
|
|
|
|
|
template<typename OBSRef> struct SignalContainer {
|
|
|
|
|
OBSRef ref;
|
|
|
|
|
vector<shared_ptr<OBSSignal>> handlers;
|
|
|
|
|
};
|
|
|
|
|
} // namespace
|
|
|
|
|
|
2014-08-02 01:33:53 -07:00
|
|
|
Q_DECLARE_METATYPE(obs_order_movement);
|
2015-12-05 05:53:38 -08:00
|
|
|
Q_DECLARE_METATYPE(SignalContainer<OBSScene>);
|
2014-02-02 14:26:23 -07:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
extern void undo_redo(const std::string &data);
|
2015-06-28 03:21:45 +02:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
obs_data_array_t *OBSBasic::SaveSceneListOrder()
|
2015-06-28 03:21:45 +02:00
|
|
|
{
|
2024-12-11 17:43:35 +01:00
|
|
|
obs_data_array_t *sceneOrder = obs_data_array_create();
|
2015-06-28 03:21:45 +02:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
for (int i = 0; i < ui->scenes->count(); i++) {
|
|
|
|
|
OBSDataAutoRelease data = obs_data_create();
|
|
|
|
|
obs_data_set_string(data, "name", QT_TO_UTF8(ui->scenes->item(i)->text()));
|
|
|
|
|
obs_data_array_push_back(sceneOrder, data);
|
2021-01-14 17:34:21 +03:00
|
|
|
}
|
|
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
return sceneOrder;
|
2014-07-27 12:48:14 -07:00
|
|
|
}
|
|
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
static void ReorderItemByName(QListWidget *lw, const char *name, int newIndex)
|
2023-03-13 03:09:52 +01:00
|
|
|
{
|
2024-12-11 17:43:35 +01:00
|
|
|
for (int i = 0; i < lw->count(); i++) {
|
|
|
|
|
QListWidgetItem *item = lw->item(i);
|
|
|
|
|
|
|
|
|
|
if (strcmp(name, QT_TO_UTF8(item->text())) == 0) {
|
|
|
|
|
if (newIndex != i) {
|
|
|
|
|
item = lw->takeItem(i);
|
|
|
|
|
lw->insertItem(newIndex, item);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
2023-03-13 03:09:52 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
void OBSBasic::LoadSceneListOrder(obs_data_array_t *array)
|
2019-02-06 13:30:02 -08:00
|
|
|
{
|
2024-12-11 17:43:35 +01:00
|
|
|
size_t num = obs_data_array_count(array);
|
2019-02-06 13:30:02 -08:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
for (size_t i = 0; i < num; i++) {
|
|
|
|
|
OBSDataAutoRelease data = obs_data_array_item(array, i);
|
|
|
|
|
const char *name = obs_data_get_string(data, "name");
|
|
|
|
|
|
|
|
|
|
ReorderItemByName(ui->scenes, name, (int)i);
|
|
|
|
|
}
|
2019-02-06 13:30:02 -08:00
|
|
|
}
|
|
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
OBSScene OBSBasic::GetCurrentScene()
|
2022-10-21 16:35:56 +02:00
|
|
|
{
|
2024-12-11 17:43:35 +01:00
|
|
|
return currentScene.load();
|
2022-10-21 16:35:56 +02:00
|
|
|
}
|
|
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
void OBSBasic::AddScene(OBSSource source)
|
2014-02-02 15:23:38 -07:00
|
|
|
{
|
2024-12-11 17:43:35 +01:00
|
|
|
const char *name = obs_source_get_name(source);
|
|
|
|
|
obs_scene_t *scene = obs_scene_from_source(source);
|
2014-10-29 15:54:45 +01:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
QListWidgetItem *item = new QListWidgetItem(QT_UTF8(name));
|
|
|
|
|
SetOBSRef(item, OBSScene(scene));
|
|
|
|
|
ui->scenes->insertItem(ui->scenes->currentRow() + 1, item);
|
2014-10-29 17:18:00 +01:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
obs_hotkey_register_source(
|
|
|
|
|
source, "OBSBasic.SelectScene", Str("Basic.Hotkeys.SelectScene"),
|
|
|
|
|
[](void *data, obs_hotkey_id, obs_hotkey_t *, bool pressed) {
|
2025-01-25 20:21:42 -06:00
|
|
|
OBSBasic *main = OBSBasic::Get();
|
2019-08-27 20:08:43 +12:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
auto potential_source = static_cast<obs_source_t *>(data);
|
|
|
|
|
OBSSourceAutoRelease source = obs_source_get_ref(potential_source);
|
|
|
|
|
if (source && pressed)
|
|
|
|
|
main->SetCurrentScene(source.Get());
|
|
|
|
|
},
|
|
|
|
|
static_cast<obs_source_t *>(source));
|
2023-02-08 03:57:51 -06:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
signal_handler_t *handler = obs_source_get_signal_handler(source);
|
2019-04-07 03:34:21 -04:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
SignalContainer<OBSScene> container;
|
|
|
|
|
container.ref = scene;
|
|
|
|
|
container.handlers.assign({
|
|
|
|
|
std::make_shared<OBSSignal>(handler, "item_add", OBSBasic::SceneItemAdded, this),
|
|
|
|
|
std::make_shared<OBSSignal>(handler, "reorder", OBSBasic::SceneReordered, this),
|
|
|
|
|
std::make_shared<OBSSignal>(handler, "refresh", OBSBasic::SceneRefreshed, this),
|
|
|
|
|
});
|
2014-04-16 22:35:01 +02:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
item->setData(static_cast<int>(QtDataRole::OBSSignals), QVariant::fromValue(container));
|
2021-10-13 13:23:11 +02:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
/* if the scene already has items (a duplicated scene) add them */
|
|
|
|
|
auto addSceneItem = [this](obs_sceneitem_t *item) {
|
|
|
|
|
AddSceneItem(item);
|
2016-01-09 14:47:58 -08:00
|
|
|
};
|
2024-07-31 10:53:59 -04:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
using addSceneItem_t = decltype(addSceneItem);
|
2014-11-01 21:48:58 +01:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
obs_scene_enum_items(
|
|
|
|
|
scene,
|
|
|
|
|
[](obs_scene_t *, obs_sceneitem_t *item, void *param) {
|
|
|
|
|
addSceneItem_t *func;
|
|
|
|
|
func = reinterpret_cast<addSceneItem_t *>(param);
|
|
|
|
|
(*func)(item);
|
|
|
|
|
return true;
|
|
|
|
|
},
|
|
|
|
|
&addSceneItem);
|
2015-07-05 23:41:36 -07:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
SaveProject();
|
2014-06-30 00:06:01 -07:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
if (!disableSaving) {
|
|
|
|
|
obs_source_t *source = obs_scene_get_source(scene);
|
|
|
|
|
blog(LOG_INFO, "User added scene '%s'", obs_source_get_name(source));
|
2014-06-30 00:06:01 -07:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
OBSProjector::UpdateMultiviewProjectors();
|
|
|
|
|
}
|
2014-07-13 21:46:23 +02:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
OnEvent(OBS_FRONTEND_EVENT_SCENE_LIST_CHANGED);
|
|
|
|
|
}
|
2019-06-12 04:56:00 -05:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
void OBSBasic::RemoveScene(OBSSource source)
|
|
|
|
|
{
|
|
|
|
|
obs_scene_t *scene = obs_scene_from_source(source);
|
2018-08-31 21:42:44 -03:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
QListWidgetItem *sel = nullptr;
|
|
|
|
|
int count = ui->scenes->count();
|
2015-04-02 23:09:13 -07:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
for (int i = 0; i < count; i++) {
|
|
|
|
|
auto item = ui->scenes->item(i);
|
|
|
|
|
auto cur_scene = GetOBSRef<OBSScene>(item);
|
|
|
|
|
if (cur_scene != scene)
|
|
|
|
|
continue;
|
2018-09-16 10:02:43 -07:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
sel = item;
|
|
|
|
|
break;
|
2017-08-12 19:45:25 +02:00
|
|
|
}
|
2018-09-16 10:02:43 -07:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
if (sel != nullptr) {
|
|
|
|
|
if (sel == ui->scenes->currentItem())
|
|
|
|
|
ui->sources->Clear();
|
|
|
|
|
delete sel;
|
|
|
|
|
}
|
2019-10-10 20:12:20 -05:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
SaveProject();
|
2021-06-27 15:30:00 -07:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
if (!disableSaving) {
|
|
|
|
|
blog(LOG_INFO, "User Removed scene '%s'", obs_source_get_name(source));
|
2022-08-18 07:50:24 -05:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
OBSProjector::UpdateMultiviewProjectors();
|
|
|
|
|
}
|
2023-02-08 03:57:51 -06:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
OnEvent(OBS_FRONTEND_EVENT_SCENE_LIST_CHANGED);
|
2014-02-02 15:23:38 -07:00
|
|
|
}
|
|
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
static bool select_one(obs_scene_t * /* scene */, obs_sceneitem_t *item, void *param)
|
2014-05-03 22:54:38 -07:00
|
|
|
{
|
2024-12-11 17:43:35 +01:00
|
|
|
obs_sceneitem_t *selectedItem = reinterpret_cast<obs_sceneitem_t *>(param);
|
|
|
|
|
if (obs_sceneitem_is_group(item))
|
|
|
|
|
obs_sceneitem_group_enum_items(item, select_one, param);
|
2015-12-05 05:58:05 -08:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
obs_sceneitem_select(item, (selectedItem == item));
|
2014-05-03 22:54:38 -07:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
return true;
|
2014-05-03 22:54:38 -07:00
|
|
|
}
|
|
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
void OBSBasic::AddSceneItem(OBSSceneItem item)
|
2014-04-26 23:47:50 -07:00
|
|
|
{
|
2024-12-11 17:43:35 +01:00
|
|
|
obs_scene_t *scene = obs_sceneitem_get_scene(item);
|
2014-04-26 23:47:50 -07:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
if (GetCurrentScene() == scene)
|
|
|
|
|
ui->sources->Add(item);
|
2015-06-23 19:29:07 -07:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
SaveProject();
|
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
|
|
|
if (!disableSaving) {
|
|
|
|
|
obs_source_t *sceneSource = obs_scene_get_source(scene);
|
|
|
|
|
obs_source_t *itemSource = obs_sceneitem_get_source(item);
|
|
|
|
|
blog(LOG_INFO, "User added source '%s' (%s) to scene '%s'", obs_source_get_name(itemSource),
|
|
|
|
|
obs_source_get_id(itemSource), obs_source_get_name(sceneSource));
|
2014-04-26 23:47:50 -07:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
obs_scene_enum_items(scene, select_one, (obs_sceneitem_t *)item);
|
|
|
|
|
}
|
2014-04-26 23:47:50 -07:00
|
|
|
}
|
|
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
void OBSBasic::DuplicateSelectedScene()
|
2015-02-17 02:45:34 -05:00
|
|
|
{
|
2024-12-11 17:43:35 +01:00
|
|
|
OBSScene curScene = GetCurrentScene();
|
2015-02-17 02:45:34 -05:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
if (!curScene)
|
|
|
|
|
return;
|
2023-02-10 14:52:15 +03:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
OBSSource curSceneSource = obs_scene_get_source(curScene);
|
|
|
|
|
QString format{obs_source_get_name(curSceneSource)};
|
|
|
|
|
format += " %1";
|
|
|
|
|
|
|
|
|
|
int i = 2;
|
|
|
|
|
QString placeHolderText = format.arg(i);
|
|
|
|
|
OBSSourceAutoRelease source = nullptr;
|
|
|
|
|
while ((source = obs_get_source_by_name(QT_TO_UTF8(placeHolderText)))) {
|
|
|
|
|
placeHolderText = format.arg(++i);
|
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.AddSceneDlg.Title"),
|
|
|
|
|
QTStr("Basic.Main.AddSceneDlg.Text"), name, placeHolderText);
|
|
|
|
|
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
|
|
|
obs_source_t *source = obs_get_source_by_name(name.c_str());
|
|
|
|
|
if (source) {
|
|
|
|
|
OBSMessageBox::warning(this, QTStr("NameExists.Title"), QTStr("NameExists.Text"));
|
2022-07-25 07:22:24 -05:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
obs_source_release(source);
|
2023-02-10 14:52:15 +03:00
|
|
|
continue;
|
2022-07-25 07:22:24 -05:00
|
|
|
}
|
2015-02-17 02:45:34 -05:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
OBSSceneAutoRelease scene = obs_scene_duplicate(curScene, name.c_str(), OBS_SCENE_DUP_REFS);
|
|
|
|
|
source = obs_scene_get_source(scene);
|
|
|
|
|
SetCurrentScene(source, true);
|
2018-01-09 18:45:20 -02:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
auto undo = [](const std::string &data) {
|
|
|
|
|
OBSSourceAutoRelease source = obs_get_source_by_name(data.c_str());
|
|
|
|
|
obs_source_remove(source);
|
|
|
|
|
};
|
2018-01-09 18:45:20 -02:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
auto redo = [this, name](const std::string &data) {
|
|
|
|
|
OBSSourceAutoRelease source = obs_get_source_by_name(data.c_str());
|
|
|
|
|
obs_scene_t *scene = obs_scene_from_source(source);
|
|
|
|
|
scene = obs_scene_duplicate(scene, name.c_str(), OBS_SCENE_DUP_REFS);
|
|
|
|
|
source = obs_scene_get_source(scene);
|
|
|
|
|
SetCurrentScene(source.Get(), true);
|
|
|
|
|
};
|
2018-01-19 22:57:36 +01:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
undo_s.add_action(QTStr("Undo.Scene.Duplicate").arg(obs_source_get_name(source)), undo, redo,
|
|
|
|
|
obs_source_get_name(source), obs_source_get_name(obs_scene_get_source(curScene)));
|
2018-01-19 22:57:36 +01:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
break;
|
2018-01-19 22:57:36 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
static bool save_undo_source_enum(obs_scene_t * /* scene */, obs_sceneitem_t *item, void *p)
|
2014-05-03 22:54:38 -07:00
|
|
|
{
|
2024-12-11 17:43:35 +01:00
|
|
|
obs_source_t *source = obs_sceneitem_get_source(item);
|
|
|
|
|
if (obs_obj_is_private(source) && !obs_source_removed(source))
|
|
|
|
|
return true;
|
2021-07-03 17:55:47 +12:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
obs_data_array_t *array = (obs_data_array_t *)p;
|
2015-06-25 03:53:48 -07:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
/* check if the source is already stored in the array */
|
|
|
|
|
const char *name = obs_source_get_name(source);
|
|
|
|
|
const size_t count = obs_data_array_count(array);
|
|
|
|
|
for (size_t i = 0; i < count; i++) {
|
|
|
|
|
OBSDataAutoRelease sourceData = obs_data_array_item(array, i);
|
|
|
|
|
if (strcmp(name, obs_data_get_string(sourceData, "name")) == 0)
|
|
|
|
|
return true;
|
2015-06-25 03:53:48 -07:00
|
|
|
}
|
|
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
if (obs_source_is_group(source))
|
|
|
|
|
obs_scene_enum_items(obs_group_from_source(source), save_undo_source_enum, p);
|
|
|
|
|
|
|
|
|
|
OBSDataAutoRelease source_data = obs_save_source(source);
|
|
|
|
|
obs_data_array_push_back(array, source_data);
|
|
|
|
|
return true;
|
2015-06-25 03:53:48 -07:00
|
|
|
}
|
|
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
static inline void RemoveSceneAndReleaseNested(obs_source_t *source)
|
2016-12-29 09:21:53 -06:00
|
|
|
{
|
2024-12-11 17:43:35 +01:00
|
|
|
obs_source_remove(source);
|
|
|
|
|
auto cb = [](void *, obs_source_t *source) {
|
|
|
|
|
if (strcmp(obs_source_get_id(source), "scene") == 0)
|
|
|
|
|
obs_scene_prune_sources(obs_scene_from_source(source));
|
|
|
|
|
return true;
|
2018-02-28 17:49:47 -03:00
|
|
|
};
|
2024-12-11 17:43:35 +01:00
|
|
|
obs_enum_scenes(cb, NULL);
|
2017-10-21 15:51:01 -02:00
|
|
|
}
|
|
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
void OBSBasic::RemoveSelectedScene()
|
2014-04-26 23:47:50 -07: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
|
|
|
OBSScene scene = GetCurrentScene();
|
2024-12-11 17:43:35 +01:00
|
|
|
obs_source_t *source = obs_scene_get_source(scene);
|
2014-05-03 22:54:38 -07:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
if (!source || !QueryRemoveSource(source)) {
|
2021-11-26 22:25:39 +13:00
|
|
|
return;
|
|
|
|
|
}
|
2015-06-23 18:47:22 -07:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
/* ------------------------------ */
|
|
|
|
|
/* save all sources in scene */
|
2015-06-23 18:47:22 -07:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
OBSDataArrayAutoRelease sources_in_deleted_scene = obs_data_array_create();
|
2015-06-23 18:47:22 -07:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
obs_scene_enum_items(scene, save_undo_source_enum, sources_in_deleted_scene);
|
2024-07-20 18:25:22 -05:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
OBSDataAutoRelease scene_data = obs_save_source(source);
|
|
|
|
|
obs_data_array_push_back(sources_in_deleted_scene, scene_data);
|
2015-09-11 21:49:21 -07:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
/* ----------------------------------------------- */
|
|
|
|
|
/* save all scenes and groups the scene is used in */
|
2024-04-23 05:05:50 +02:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
OBSDataArrayAutoRelease scene_used_in_other_scenes = obs_data_array_create();
|
2015-09-11 21:49:21 -07:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
struct other_scenes_cb_data {
|
|
|
|
|
obs_source_t *oldScene;
|
|
|
|
|
obs_data_array_t *scene_used_in_other_scenes;
|
|
|
|
|
} other_scenes_cb_data;
|
|
|
|
|
other_scenes_cb_data.oldScene = source;
|
|
|
|
|
other_scenes_cb_data.scene_used_in_other_scenes = scene_used_in_other_scenes;
|
2015-09-11 21:49:21 -07:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
auto other_scenes_cb = [](void *data_ptr, obs_source_t *scene) {
|
|
|
|
|
struct other_scenes_cb_data *data = (struct other_scenes_cb_data *)data_ptr;
|
|
|
|
|
if (strcmp(obs_source_get_name(scene), obs_source_get_name(data->oldScene)) == 0)
|
|
|
|
|
return true;
|
|
|
|
|
obs_sceneitem_t *item = obs_scene_find_source(obs_group_or_scene_from_source(scene),
|
|
|
|
|
obs_source_get_name(data->oldScene));
|
|
|
|
|
if (item) {
|
|
|
|
|
OBSDataAutoRelease scene_data =
|
|
|
|
|
obs_save_source(obs_scene_get_source(obs_sceneitem_get_scene(item)));
|
|
|
|
|
obs_data_array_push_back(data->scene_used_in_other_scenes, scene_data);
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
};
|
|
|
|
|
obs_enum_scenes(other_scenes_cb, &other_scenes_cb_data);
|
2015-09-11 21:49:21 -07:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
/* --------------------------- */
|
|
|
|
|
/* undo/redo */
|
2015-09-11 21:49:21 -07:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
auto undo = [this](const std::string &json) {
|
|
|
|
|
OBSDataAutoRelease base = obs_data_create_from_json(json.c_str());
|
|
|
|
|
OBSDataArrayAutoRelease sources_in_deleted_scene = obs_data_get_array(base, "sources_in_deleted_scene");
|
|
|
|
|
OBSDataArrayAutoRelease scene_used_in_other_scenes =
|
|
|
|
|
obs_data_get_array(base, "scene_used_in_other_scenes");
|
|
|
|
|
int savedIndex = (int)obs_data_get_int(base, "index");
|
|
|
|
|
std::vector<OBSSource> sources;
|
2015-06-30 05:49:31 -07:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
/* create missing sources */
|
|
|
|
|
size_t count = obs_data_array_count(sources_in_deleted_scene);
|
|
|
|
|
sources.reserve(count);
|
2014-05-03 22:54:38 -07:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
for (size_t i = 0; i < count; i++) {
|
|
|
|
|
OBSDataAutoRelease data = obs_data_array_item(sources_in_deleted_scene, i);
|
|
|
|
|
const char *name = obs_data_get_string(data, "name");
|
2015-06-25 03:53:48 -07:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
OBSSourceAutoRelease source = obs_get_source_by_name(name);
|
|
|
|
|
if (!source) {
|
|
|
|
|
source = obs_load_source(data);
|
|
|
|
|
sources.push_back(source.Get());
|
2015-06-25 03:53:48 -07:00
|
|
|
}
|
|
|
|
|
}
|
2016-08-05 17:43:30 -07:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
/* actually load sources now */
|
|
|
|
|
for (obs_source_t *source : sources)
|
|
|
|
|
obs_source_load2(source);
|
2016-08-05 17:43:30 -07:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
/* Add scene to scenes and groups it was nested in */
|
|
|
|
|
for (size_t i = 0; i < obs_data_array_count(scene_used_in_other_scenes); i++) {
|
|
|
|
|
OBSDataAutoRelease data = obs_data_array_item(scene_used_in_other_scenes, i);
|
|
|
|
|
const char *name = obs_data_get_string(data, "name");
|
|
|
|
|
OBSSourceAutoRelease source = obs_get_source_by_name(name);
|
2016-08-05 17:43:30 -07:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
OBSDataAutoRelease settings = obs_data_get_obj(data, "settings");
|
|
|
|
|
OBSDataArrayAutoRelease items = obs_data_get_array(settings, "items");
|
2016-08-05 17:43:30 -07:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
/* Clear scene, but keep a reference to all sources in the scene to make sure they don't get destroyed */
|
|
|
|
|
std::vector<OBSSource> existing_sources;
|
|
|
|
|
auto cb = [](obs_scene_t *, obs_sceneitem_t *item, void *data) {
|
|
|
|
|
std::vector<OBSSource> *existing = (std::vector<OBSSource> *)data;
|
|
|
|
|
OBSSource source = obs_sceneitem_get_source(item);
|
|
|
|
|
obs_sceneitem_remove(item);
|
|
|
|
|
existing->push_back(source);
|
|
|
|
|
return true;
|
|
|
|
|
};
|
|
|
|
|
obs_scene_enum_items(obs_group_or_scene_from_source(source), cb, (void *)&existing_sources);
|
2016-08-05 17:43:30 -07:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
/* Re-add sources to the scene */
|
|
|
|
|
obs_sceneitems_add(obs_group_or_scene_from_source(source), items);
|
2024-07-29 22:46:38 +02:00
|
|
|
}
|
|
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
obs_source_t *scene_source = sources.back();
|
|
|
|
|
OBSScene scene = obs_scene_from_source(scene_source);
|
|
|
|
|
SetCurrentScene(scene, true);
|
2021-03-22 01:20:11 -04:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
/* set original index in list box */
|
|
|
|
|
ui->scenes->blockSignals(true);
|
|
|
|
|
int curIndex = ui->scenes->currentRow();
|
|
|
|
|
QListWidgetItem *item = ui->scenes->takeItem(curIndex);
|
|
|
|
|
ui->scenes->insertItem(savedIndex, item);
|
|
|
|
|
ui->scenes->setCurrentRow(savedIndex);
|
|
|
|
|
currentScene = scene.Get();
|
|
|
|
|
ui->scenes->blockSignals(false);
|
|
|
|
|
};
|
2022-07-20 03:44:43 -05:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
auto redo = [](const std::string &name) {
|
|
|
|
|
OBSSourceAutoRelease source = obs_get_source_by_name(name.c_str());
|
|
|
|
|
RemoveSceneAndReleaseNested(source);
|
|
|
|
|
};
|
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
|
|
|
|
2021-11-26 22:25:39 +13:00
|
|
|
OBSDataAutoRelease data = obs_data_create();
|
2024-12-11 17:43:35 +01:00
|
|
|
obs_data_set_array(data, "sources_in_deleted_scene", sources_in_deleted_scene);
|
|
|
|
|
obs_data_set_array(data, "scene_used_in_other_scenes", scene_used_in_other_scenes);
|
|
|
|
|
obs_data_set_int(data, "index", ui->scenes->currentRow());
|
2018-08-02 08:23:12 +12:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
const char *scene_name = obs_source_get_name(source);
|
|
|
|
|
undo_s.add_action(QTStr("Undo.Delete").arg(scene_name), undo, redo, obs_data_get_json(data), scene_name);
|
2018-08-02 08:23:12 +12:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
/* --------------------------- */
|
|
|
|
|
/* remove */
|
2017-05-10 19:37:45 -07:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
RemoveSceneAndReleaseNested(source);
|
2017-05-13 20:45:48 -07:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
OnEvent(OBS_FRONTEND_EVENT_SCENE_LIST_CHANGED);
|
2017-05-10 19:37:45 -07:00
|
|
|
}
|
2018-08-02 08:23:12 +12:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
void OBSBasic::SceneReordered(void *data, calldata_t *params)
|
2018-08-18 01:13:20 -05:00
|
|
|
{
|
2024-12-11 17:43:35 +01:00
|
|
|
OBSBasic *window = static_cast<OBSBasic *>(data);
|
2018-08-18 01:13:20 -05:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
obs_scene_t *scene = (obs_scene_t *)calldata_ptr(params, "scene");
|
2018-08-18 01:13:20 -05:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
QMetaObject::invokeMethod(window, "ReorderSources", Q_ARG(OBSScene, OBSScene(scene)));
|
2018-08-18 01:13:20 -05:00
|
|
|
}
|
|
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
void OBSBasic::SceneRefreshed(void *data, calldata_t *params)
|
2022-09-02 22:52:35 +02:00
|
|
|
{
|
2024-12-11 17:43:35 +01:00
|
|
|
OBSBasic *window = static_cast<OBSBasic *>(data);
|
2022-09-02 22:52:35 +02:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
obs_scene_t *scene = (obs_scene_t *)calldata_ptr(params, "scene");
|
2022-09-02 22:52:35 +02:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
QMetaObject::invokeMethod(window, "RefreshSources", Q_ARG(OBSScene, OBSScene(scene)));
|
|
|
|
|
}
|
2022-09-02 22:52:35 +02:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
void OBSBasic::SceneItemAdded(void *data, calldata_t *params)
|
|
|
|
|
{
|
|
|
|
|
OBSBasic *window = static_cast<OBSBasic *>(data);
|
2022-09-02 22:52:35 +02:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
obs_sceneitem_t *item = (obs_sceneitem_t *)calldata_ptr(params, "item");
|
2022-09-02 22:52:35 +02:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
QMetaObject::invokeMethod(window, "AddSceneItem", Q_ARG(OBSSceneItem, OBSSceneItem(item)));
|
2022-09-02 22:52:35 +02:00
|
|
|
}
|
|
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
void OBSBasic::on_scenes_currentItemChanged(QListWidgetItem *current, QListWidgetItem *)
|
2019-02-06 13:57:59 -08:00
|
|
|
{
|
2024-12-11 17:43:35 +01:00
|
|
|
OBSSource source;
|
2022-10-22 09:49:27 +02:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
if (current) {
|
|
|
|
|
OBSScene scene = GetOBSRef<OBSScene>(current);
|
|
|
|
|
source = obs_scene_get_source(scene);
|
2022-10-21 16:53:06 +02:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
currentScene = scene;
|
|
|
|
|
} else {
|
|
|
|
|
currentScene = NULL;
|
2019-02-06 13:57:59 -08:00
|
|
|
}
|
|
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
SetCurrentScene(source);
|
2022-10-22 09:49:27 +02:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
if (vcamEnabled && vcamConfig.type == VCamOutputType::PreviewOutput)
|
|
|
|
|
outputHandler->UpdateVirtualCamOutputSource();
|
2022-10-22 09:49:27 +02:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
OnEvent(OBS_FRONTEND_EVENT_PREVIEW_SCENE_CHANGED);
|
2022-10-22 09:49:27 +02:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
UpdateContextBar();
|
2022-10-22 09:49:27 +02:00
|
|
|
}
|
|
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
void OBSBasic::EditSceneName()
|
2022-10-21 16:53:06 +02:00
|
|
|
{
|
2024-12-11 17:43:35 +01:00
|
|
|
ui->scenesDock->removeAction(renameScene);
|
|
|
|
|
QListWidgetItem *item = ui->scenes->currentItem();
|
|
|
|
|
Qt::ItemFlags flags = item->flags();
|
2022-10-21 16:53:06 +02:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
item->setFlags(flags | Qt::ItemIsEditable);
|
|
|
|
|
ui->scenes->editItem(item);
|
|
|
|
|
item->setFlags(flags);
|
|
|
|
|
}
|
2022-10-21 16:53:06 +02:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
void OBSBasic::on_scenes_customContextMenuRequested(const QPoint &pos)
|
|
|
|
|
{
|
|
|
|
|
QListWidgetItem *item = ui->scenes->itemAt(pos);
|
2022-10-21 16:53:06 +02:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
QMenu popup(this);
|
|
|
|
|
QMenu order(QTStr("Basic.MainMenu.Edit.Order"), this);
|
2022-10-21 16:53:06 +02:00
|
|
|
|
2025-04-13 12:45:17 -07:00
|
|
|
popup.addAction(QTStr("AddScene") + "...", this, &OBSBasic::on_actionAddScene_triggered);
|
2022-10-21 16:53:06 +02:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
if (item) {
|
|
|
|
|
QAction *copyFilters = new QAction(QTStr("Copy.Filters"), this);
|
|
|
|
|
copyFilters->setEnabled(false);
|
|
|
|
|
connect(copyFilters, &QAction::triggered, this, &OBSBasic::SceneCopyFilters);
|
|
|
|
|
QAction *pasteFilters = new QAction(QTStr("Paste.Filters"), this);
|
|
|
|
|
pasteFilters->setEnabled(!obs_weak_source_expired(copyFiltersSource));
|
|
|
|
|
connect(pasteFilters, &QAction::triggered, this, &OBSBasic::ScenePasteFilters);
|
2022-10-21 16:53:06 +02:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
popup.addSeparator();
|
|
|
|
|
popup.addAction(QTStr("Duplicate"), this, &OBSBasic::DuplicateSelectedScene);
|
|
|
|
|
popup.addAction(copyFilters);
|
|
|
|
|
popup.addAction(pasteFilters);
|
|
|
|
|
popup.addSeparator();
|
|
|
|
|
popup.addAction(renameScene);
|
|
|
|
|
popup.addAction(ui->actionRemoveScene);
|
|
|
|
|
popup.addSeparator();
|
2022-10-21 16:53:06 +02:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
order.addAction(QTStr("Basic.MainMenu.Edit.Order.MoveUp"), this, &OBSBasic::on_actionSceneUp_triggered);
|
|
|
|
|
order.addAction(QTStr("Basic.MainMenu.Edit.Order.MoveDown"), this,
|
|
|
|
|
&OBSBasic::on_actionSceneDown_triggered);
|
|
|
|
|
order.addSeparator();
|
|
|
|
|
order.addAction(QTStr("Basic.MainMenu.Edit.Order.MoveToTop"), this, &OBSBasic::MoveSceneToTop);
|
|
|
|
|
order.addAction(QTStr("Basic.MainMenu.Edit.Order.MoveToBottom"), this, &OBSBasic::MoveSceneToBottom);
|
|
|
|
|
popup.addMenu(&order);
|
2022-10-21 16:53:06 +02:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
popup.addSeparator();
|
2022-10-21 16:53:06 +02:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
delete sceneProjectorMenu;
|
2024-11-04 12:33:14 -05:00
|
|
|
sceneProjectorMenu = new QMenu(QTStr("Projector.Open.Scene"));
|
2024-12-11 17:43:35 +01:00
|
|
|
AddProjectorMenuMonitors(sceneProjectorMenu, this, &OBSBasic::OpenSceneProjector);
|
2024-11-04 12:33:14 -05:00
|
|
|
sceneProjectorMenu->addSeparator();
|
|
|
|
|
sceneProjectorMenu->addAction(QTStr("Projector.Window"), this, &OBSBasic::OpenSceneWindow);
|
2022-10-22 09:49:27 +02:00
|
|
|
|
2024-11-04 12:33:14 -05:00
|
|
|
popup.addMenu(sceneProjectorMenu);
|
|
|
|
|
popup.addSeparator();
|
2022-10-22 11:51:30 +02:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
popup.addAction(QTStr("Screenshot.Scene"), this, &OBSBasic::ScreenshotScene);
|
|
|
|
|
popup.addSeparator();
|
|
|
|
|
popup.addAction(QTStr("Filters"), this, &OBSBasic::OpenSceneFilters);
|
2022-10-22 11:51:30 +02:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
popup.addSeparator();
|
2022-10-22 11:51:30 +02:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
delete perSceneTransitionMenu;
|
|
|
|
|
perSceneTransitionMenu = CreatePerSceneTransitionMenu();
|
|
|
|
|
popup.addMenu(perSceneTransitionMenu);
|
2022-10-22 11:51:30 +02:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
/* ---------------------- */
|
2022-10-22 11:51:30 +02:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
QAction *multiviewAction = popup.addAction(QTStr("ShowInMultiview"));
|
2022-10-22 11:51:30 +02:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
OBSSource source = GetCurrentSceneSource();
|
|
|
|
|
OBSDataAutoRelease data = obs_source_get_private_settings(source);
|
2022-10-22 11:51:30 +02:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
obs_data_set_default_bool(data, "show_in_multiview", true);
|
|
|
|
|
bool show = obs_data_get_bool(data, "show_in_multiview");
|
2022-10-22 11:51:30 +02:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
multiviewAction->setCheckable(true);
|
|
|
|
|
multiviewAction->setChecked(show);
|
2019-02-18 19:28:26 -08:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
auto showInMultiview = [](OBSData data) {
|
|
|
|
|
bool show = obs_data_get_bool(data, "show_in_multiview");
|
|
|
|
|
obs_data_set_bool(data, "show_in_multiview", !show);
|
|
|
|
|
OBSProjector::UpdateMultiviewProjectors();
|
|
|
|
|
};
|
2019-02-18 19:28:26 -08:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
connect(multiviewAction, &QAction::triggered, std::bind(showInMultiview, data.Get()));
|
2019-02-18 19:28:26 -08:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
copyFilters->setEnabled(obs_source_filter_count(source) > 0);
|
|
|
|
|
}
|
2019-04-07 03:34:21 -04:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
popup.addSeparator();
|
2021-11-03 21:11:06 +01:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
bool grid = ui->scenes->GetGridMode();
|
2019-04-07 03:34:21 -04:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
QAction *gridAction = new QAction(grid ? QTStr("Basic.Main.ListMode") : QTStr("Basic.Main.GridMode"), this);
|
|
|
|
|
connect(gridAction, &QAction::triggered, this, &OBSBasic::GridActionClicked);
|
|
|
|
|
popup.addAction(gridAction);
|
|
|
|
|
|
|
|
|
|
popup.exec(QCursor::pos());
|
2019-04-07 03:34:21 -04:00
|
|
|
}
|
2019-04-23 15:56:30 +08:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
void OBSBasic::on_actionSceneListMode_triggered()
|
2019-04-23 15:56:30 +08:00
|
|
|
{
|
2024-12-11 17:43:35 +01:00
|
|
|
ui->scenes->SetGridMode(false);
|
|
|
|
|
config_set_bool(App()->GetUserConfig(), "BasicWindow", "gridMode", false);
|
2019-04-23 15:56:30 +08:00
|
|
|
}
|
2019-05-05 20:00:06 -07:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
void OBSBasic::on_actionSceneGridMode_triggered()
|
2019-05-05 20:00:06 -07:00
|
|
|
{
|
2024-12-11 17:43:35 +01:00
|
|
|
ui->scenes->SetGridMode(true);
|
|
|
|
|
config_set_bool(App()->GetUserConfig(), "BasicWindow", "gridMode", true);
|
2019-05-05 20:00:06 -07:00
|
|
|
}
|
2019-07-07 15:47:29 -07:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
void OBSBasic::GridActionClicked()
|
2019-07-07 15:47:29 -07:00
|
|
|
{
|
2024-12-11 17:43:35 +01:00
|
|
|
bool gridMode = !ui->scenes->GetGridMode();
|
|
|
|
|
ui->scenes->SetGridMode(gridMode);
|
2019-10-02 19:44:47 -04:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
if (gridMode)
|
|
|
|
|
ui->actionSceneGridMode->setChecked(true);
|
|
|
|
|
else
|
|
|
|
|
ui->actionSceneListMode->setChecked(true);
|
2019-07-07 15:47:29 -07:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
config_set_bool(App()->GetUserConfig(), "BasicWindow", "gridMode", gridMode);
|
2019-07-07 15:47:29 -07:00
|
|
|
}
|
|
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
void OBSBasic::on_actionAddScene_triggered()
|
2019-07-07 15:47:29 -07:00
|
|
|
{
|
2024-12-11 17:43:35 +01:00
|
|
|
string name;
|
|
|
|
|
QString format{QTStr("Basic.Main.DefaultSceneName.Text")};
|
2019-07-07 15:47:29 -07:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
int i = 2;
|
|
|
|
|
QString placeHolderText = format.arg(i);
|
|
|
|
|
OBSSourceAutoRelease source = nullptr;
|
|
|
|
|
while ((source = obs_get_source_by_name(QT_TO_UTF8(placeHolderText)))) {
|
|
|
|
|
placeHolderText = format.arg(++i);
|
|
|
|
|
}
|
2019-07-07 15:47:29 -07:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
bool accepted = NameDialog::AskForName(this, QTStr("Basic.Main.AddSceneDlg.Title"),
|
|
|
|
|
QTStr("Basic.Main.AddSceneDlg.Text"), name, placeHolderText);
|
2024-02-29 20:46:07 +00:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
if (accepted) {
|
|
|
|
|
if (name.empty()) {
|
|
|
|
|
OBSMessageBox::warning(this, QTStr("NoNameEntered.Title"), QTStr("NoNameEntered.Text"));
|
|
|
|
|
on_actionAddScene_triggered();
|
|
|
|
|
return;
|
|
|
|
|
}
|
2019-10-02 19:44:47 -04:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
OBSSourceAutoRelease source = obs_get_source_by_name(name.c_str());
|
|
|
|
|
if (source) {
|
|
|
|
|
OBSMessageBox::warning(this, QTStr("NameExists.Title"), QTStr("NameExists.Text"));
|
2020-04-14 20:16:33 +02:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
on_actionAddScene_triggered();
|
|
|
|
|
return;
|
2020-11-12 23:39:32 +01:00
|
|
|
}
|
2019-10-02 19:44:47 -04:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
auto undo_fn = [](const std::string &data) {
|
|
|
|
|
obs_source_t *t = obs_get_source_by_name(data.c_str());
|
|
|
|
|
if (t) {
|
|
|
|
|
obs_source_remove(t);
|
|
|
|
|
obs_source_release(t);
|
|
|
|
|
}
|
|
|
|
|
};
|
2019-07-07 15:47:29 -07:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
auto redo_fn = [this](const std::string &data) {
|
|
|
|
|
OBSSceneAutoRelease scene = obs_scene_create(data.c_str());
|
|
|
|
|
obs_source_t *source = obs_scene_get_source(scene);
|
|
|
|
|
SetCurrentScene(source, true);
|
|
|
|
|
};
|
|
|
|
|
undo_s.add_action(QTStr("Undo.Add").arg(QString(name.c_str())), undo_fn, redo_fn, name, name);
|
2019-07-07 15:47:29 -07:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
OBSSceneAutoRelease scene = obs_scene_create(name.c_str());
|
|
|
|
|
obs_source_t *scene_source = obs_scene_get_source(scene);
|
|
|
|
|
SetCurrentScene(scene_source);
|
|
|
|
|
}
|
2019-07-07 15:47:29 -07:00
|
|
|
}
|
|
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
void OBSBasic::on_actionRemoveScene_triggered()
|
2019-07-07 15:47:29 -07:00
|
|
|
{
|
2024-12-11 17:43:35 +01:00
|
|
|
RemoveSelectedScene();
|
2024-04-03 17:08:14 +02:00
|
|
|
}
|
|
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
void OBSBasic::ChangeSceneIndex(bool relative, int offset, int invalidIdx)
|
2019-09-27 15:04:07 +02:00
|
|
|
{
|
2024-12-11 17:43:35 +01:00
|
|
|
int idx = ui->scenes->currentRow();
|
|
|
|
|
if (idx == -1 || idx == invalidIdx)
|
|
|
|
|
return;
|
2019-09-27 15:04:07 +02:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
ui->scenes->blockSignals(true);
|
|
|
|
|
QListWidgetItem *item = ui->scenes->takeItem(idx);
|
2019-09-27 15:04:07 +02:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
if (!relative)
|
|
|
|
|
idx = 0;
|
2019-09-27 15:04:07 +02:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
ui->scenes->insertItem(idx + offset, item);
|
|
|
|
|
ui->scenes->setCurrentRow(idx + offset);
|
|
|
|
|
item->setSelected(true);
|
|
|
|
|
currentScene = GetOBSRef<OBSScene>(item).Get();
|
|
|
|
|
ui->scenes->blockSignals(false);
|
2019-12-27 17:12:31 +08:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
OBSProjector::UpdateMultiviewProjectors();
|
2019-12-27 17:12:31 +08:00
|
|
|
}
|
|
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
void OBSBasic::on_actionSceneUp_triggered()
|
2019-12-27 17:12:31 +08:00
|
|
|
{
|
2024-12-11 17:43:35 +01:00
|
|
|
ChangeSceneIndex(true, -1, 0);
|
2024-03-28 16:43:12 -04:00
|
|
|
}
|
|
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
void OBSBasic::on_actionSceneDown_triggered()
|
2024-03-28 16:43:12 -04:00
|
|
|
{
|
2024-12-11 17:43:35 +01:00
|
|
|
ChangeSceneIndex(true, 1, ui->scenes->count() - 1);
|
2019-12-27 17:12:31 +08:00
|
|
|
}
|
|
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
void OBSBasic::MoveSceneToTop()
|
2019-06-12 04:56:00 -05:00
|
|
|
{
|
2024-12-11 17:43:35 +01:00
|
|
|
ChangeSceneIndex(false, 0, 0);
|
2019-06-12 04:56:00 -05:00
|
|
|
}
|
|
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
void OBSBasic::MoveSceneToBottom()
|
2019-06-12 04:56:00 -05:00
|
|
|
{
|
2024-12-11 17:43:35 +01:00
|
|
|
ChangeSceneIndex(false, ui->scenes->count() - 1, ui->scenes->count() - 1);
|
2019-06-12 04:56:00 -05:00
|
|
|
}
|
|
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
void OBSBasic::EditSceneItemName()
|
2019-06-12 04:56:00 -05:00
|
|
|
{
|
2024-12-11 17:43:35 +01:00
|
|
|
int idx = GetTopSelectedSourceItem();
|
|
|
|
|
ui->sources->Edit(idx);
|
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_scenes_itemDoubleClicked(QListWidgetItem *witem)
|
2019-10-15 23:20:35 -05:00
|
|
|
{
|
2024-12-11 17:43:35 +01:00
|
|
|
if (!witem)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (IsPreviewProgramMode()) {
|
|
|
|
|
bool doubleClickSwitch =
|
|
|
|
|
config_get_bool(App()->GetUserConfig(), "BasicWindow", "TransitionOnDoubleClick");
|
2019-10-15 23:20:35 -05:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
if (doubleClickSwitch)
|
|
|
|
|
TransitionClicked();
|
2023-10-04 17:32:44 -04:00
|
|
|
}
|
2019-10-15 23:20:35 -05:00
|
|
|
}
|
2020-02-14 23:30:34 +11:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
OBSData OBSBasic::BackupScene(obs_scene_t *scene, std::vector<obs_source_t *> *sources)
|
2020-02-14 23:30:34 +11:00
|
|
|
{
|
2024-12-11 17:43:35 +01:00
|
|
|
OBSDataArrayAutoRelease undo_array = obs_data_array_create();
|
2022-02-22 00:40:17 +11:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
if (!sources) {
|
|
|
|
|
obs_scene_enum_items(scene, save_undo_source_enum, undo_array);
|
|
|
|
|
} else {
|
|
|
|
|
for (obs_source_t *source : *sources) {
|
|
|
|
|
obs_data_t *source_data = obs_save_source(source);
|
|
|
|
|
obs_data_array_push_back(undo_array, source_data);
|
|
|
|
|
obs_data_release(source_data);
|
2022-02-22 00:40:17 +11:00
|
|
|
}
|
|
|
|
|
}
|
2020-06-13 21:00:55 -05:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
OBSDataAutoRelease scene_data = obs_save_source(obs_scene_get_source(scene));
|
|
|
|
|
obs_data_array_push_back(undo_array, scene_data);
|
2020-06-13 21:00:55 -05:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
OBSDataAutoRelease data = obs_data_create();
|
2018-08-23 20:43:44 -05:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
obs_data_set_array(data, "array", undo_array);
|
|
|
|
|
obs_data_get_json(data);
|
|
|
|
|
return data.Get();
|
2020-08-23 05:12:46 -05:00
|
|
|
}
|
|
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
static bool add_source_enum(obs_scene_t *, obs_sceneitem_t *item, void *p)
|
2018-08-23 20:43:44 -05:00
|
|
|
{
|
2024-12-11 17:43:35 +01:00
|
|
|
auto sources = static_cast<std::vector<OBSSource> *>(p);
|
|
|
|
|
sources->push_back(obs_sceneitem_get_source(item));
|
|
|
|
|
return true;
|
2018-08-23 20:43:44 -05:00
|
|
|
}
|
|
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
void OBSBasic::CreateSceneUndoRedoAction(const QString &action_name, OBSData undo_data, OBSData redo_data)
|
2018-08-23 20:43:44 -05:00
|
|
|
{
|
2024-12-11 17:43:35 +01:00
|
|
|
auto undo_redo = [this](const std::string &json) {
|
|
|
|
|
OBSDataAutoRelease base = obs_data_create_from_json(json.c_str());
|
|
|
|
|
OBSDataArrayAutoRelease array = obs_data_get_array(base, "array");
|
|
|
|
|
std::vector<OBSSource> sources;
|
|
|
|
|
std::vector<OBSSource> old_sources;
|
2021-01-11 23:26:02 -06:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
/* create missing sources */
|
|
|
|
|
const size_t count = obs_data_array_count(array);
|
|
|
|
|
sources.reserve(count);
|
2022-09-09 07:20:48 -05:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
for (size_t i = 0; i < count; i++) {
|
|
|
|
|
OBSDataAutoRelease data = obs_data_array_item(array, i);
|
|
|
|
|
const char *name = obs_data_get_string(data, "name");
|
2022-09-09 07:20:48 -05:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
OBSSourceAutoRelease source = obs_get_source_by_name(name);
|
|
|
|
|
if (!source)
|
|
|
|
|
source = obs_load_source(data);
|
2021-02-04 06:01:35 -06:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
sources.push_back(source.Get());
|
2021-08-19 23:56:49 -05:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
/* update scene/group settings to restore their
|
|
|
|
|
* contents to their saved settings */
|
|
|
|
|
obs_scene_t *scene = obs_group_or_scene_from_source(source);
|
|
|
|
|
if (scene) {
|
|
|
|
|
obs_scene_enum_items(scene, add_source_enum, &old_sources);
|
|
|
|
|
OBSDataAutoRelease scene_settings = obs_data_get_obj(data, "settings");
|
|
|
|
|
obs_source_update(source, scene_settings);
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-06-27 02:31:08 +02:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
/* actually load sources now */
|
|
|
|
|
for (obs_source_t *source : sources)
|
|
|
|
|
obs_source_load2(source);
|
|
|
|
|
|
|
|
|
|
ui->sources->RefreshItems();
|
|
|
|
|
};
|
2023-02-10 13:27:08 +01:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
const char *undo_json = obs_data_get_last_json(undo_data);
|
|
|
|
|
const char *redo_json = obs_data_get_last_json(redo_data);
|
|
|
|
|
|
|
|
|
|
undo_s.add_action(action_name, undo_redo, undo_redo, undo_json, redo_json);
|
2023-02-10 13:27:08 +01:00
|
|
|
}
|
|
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
void OBSBasic::MoveSceneItem(enum obs_order_movement movement, const QString &action_name)
|
2020-06-27 02:31:08 +02:00
|
|
|
{
|
2024-12-11 17:43:35 +01:00
|
|
|
OBSSceneItem item = GetCurrentSceneItem();
|
|
|
|
|
obs_source_t *source = obs_sceneitem_get_source(item);
|
|
|
|
|
|
|
|
|
|
if (!source)
|
2020-06-27 02:31:08 +02:00
|
|
|
return;
|
|
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
OBSScene scene = GetCurrentScene();
|
|
|
|
|
std::vector<obs_source_t *> sources;
|
|
|
|
|
if (scene != obs_sceneitem_get_scene(item))
|
|
|
|
|
sources.push_back(obs_scene_get_source(obs_sceneitem_get_scene(item)));
|
2020-06-27 02:31:08 +02:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
OBSData undo_data = BackupScene(scene, &sources);
|
2020-06-27 02:31:08 +02:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
obs_sceneitem_set_order(item, movement);
|
2020-06-27 02:31:08 +02:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
const char *source_name = obs_source_get_name(source);
|
|
|
|
|
const char *scene_name = obs_source_get_name(obs_scene_get_source(scene));
|
2022-01-27 02:37:32 +01:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
OBSData redo_data = BackupScene(scene, &sources);
|
|
|
|
|
CreateSceneUndoRedoAction(action_name.arg(source_name, scene_name), undo_data, redo_data);
|
2020-06-27 02:31:08 +02:00
|
|
|
}
|
2021-07-03 17:55:47 +12:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
static void RenameListItem(OBSBasic *parent, QListWidget *listWidget, obs_source_t *source, const string &name)
|
2021-07-03 17:55:47 +12:00
|
|
|
{
|
2024-12-11 17:43:35 +01:00
|
|
|
const char *prevName = obs_source_get_name(source);
|
|
|
|
|
if (name == prevName)
|
|
|
|
|
return;
|
2021-07-03 17:55:47 +12:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
OBSSourceAutoRelease foundSource = obs_get_source_by_name(name.c_str());
|
|
|
|
|
QListWidgetItem *listItem = listWidget->currentItem();
|
2021-07-03 17:55:47 +12:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
if (foundSource || name.empty()) {
|
|
|
|
|
listItem->setText(QT_UTF8(prevName));
|
2021-07-03 17:55:47 +12:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
if (foundSource) {
|
|
|
|
|
OBSMessageBox::warning(parent, QTStr("NameExists.Title"), QTStr("NameExists.Text"));
|
|
|
|
|
} else if (name.empty()) {
|
|
|
|
|
OBSMessageBox::warning(parent, QTStr("NoNameEntered.Title"), QTStr("NoNameEntered.Text"));
|
|
|
|
|
}
|
2021-07-03 17:55:47 +12:00
|
|
|
} else {
|
2024-12-11 17:43:35 +01:00
|
|
|
auto undo = [prev = std::string(prevName)](const std::string &data) {
|
|
|
|
|
OBSSourceAutoRelease source = obs_get_source_by_uuid(data.c_str());
|
|
|
|
|
obs_source_set_name(source, prev.c_str());
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
auto redo = [name](const std::string &data) {
|
|
|
|
|
OBSSourceAutoRelease source = obs_get_source_by_uuid(data.c_str());
|
|
|
|
|
obs_source_set_name(source, name.c_str());
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
std::string source_uuid(obs_source_get_uuid(source));
|
|
|
|
|
parent->undo_s.add_action(QTStr("Undo.Rename").arg(name.c_str()), undo, redo, source_uuid, source_uuid);
|
|
|
|
|
|
|
|
|
|
listItem->setText(QT_UTF8(name.c_str()));
|
|
|
|
|
obs_source_set_name(source, name.c_str());
|
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::SceneNameEdited(QWidget *editor)
|
2021-11-01 02:16:56 -05:00
|
|
|
{
|
2024-12-11 17:43:35 +01:00
|
|
|
OBSScene scene = GetCurrentScene();
|
|
|
|
|
QLineEdit *edit = qobject_cast<QLineEdit *>(editor);
|
|
|
|
|
string text = QT_TO_UTF8(edit->text().trimmed());
|
2022-07-29 20:31:13 +02:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
if (!scene)
|
|
|
|
|
return;
|
2024-06-18 14:18:22 -05:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
obs_source_t *source = obs_scene_get_source(scene);
|
|
|
|
|
RenameListItem(this, ui->scenes, source, text);
|
2024-07-31 10:53:59 -04:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
ui->scenesDock->addAction(renameScene);
|
2024-07-31 10:53:59 -04:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
OnEvent(OBS_FRONTEND_EVENT_SCENE_LIST_CHANGED);
|
2024-07-31 10:53:59 -04:00
|
|
|
}
|
|
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
void OBSBasic::OpenSceneFilters()
|
2024-07-31 10:53:59 -04:00
|
|
|
{
|
2024-12-11 17:43:35 +01:00
|
|
|
OBSScene scene = GetCurrentScene();
|
|
|
|
|
OBSSource source = obs_scene_get_source(scene);
|
|
|
|
|
|
|
|
|
|
CreateFiltersWindow(source);
|
2024-07-31 10:53:59 -04:00
|
|
|
}
|
|
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
static bool reset_tr(obs_scene_t * /* scene */, obs_sceneitem_t *item, void *)
|
2024-07-31 10:53:59 -04:00
|
|
|
{
|
2024-12-11 17:43:35 +01:00
|
|
|
if (obs_sceneitem_is_group(item))
|
|
|
|
|
obs_sceneitem_group_enum_items(item, reset_tr, nullptr);
|
|
|
|
|
if (!obs_sceneitem_selected(item))
|
|
|
|
|
return true;
|
|
|
|
|
if (obs_sceneitem_locked(item))
|
|
|
|
|
return true;
|
2024-09-03 16:29:30 +02:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
obs_sceneitem_defer_update_begin(item);
|
2024-09-03 16:29:30 +02:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
obs_transform_info info;
|
|
|
|
|
vec2_set(&info.pos, 0.0f, 0.0f);
|
|
|
|
|
vec2_set(&info.scale, 1.0f, 1.0f);
|
|
|
|
|
info.rot = 0.0f;
|
|
|
|
|
info.alignment = OBS_ALIGN_TOP | OBS_ALIGN_LEFT;
|
|
|
|
|
info.bounds_type = OBS_BOUNDS_NONE;
|
|
|
|
|
info.bounds_alignment = OBS_ALIGN_CENTER;
|
|
|
|
|
info.crop_to_bounds = false;
|
|
|
|
|
vec2_set(&info.bounds, 0.0f, 0.0f);
|
|
|
|
|
obs_sceneitem_set_info2(item, &info);
|
2024-09-03 16:29:30 +02:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
obs_sceneitem_crop crop = {};
|
|
|
|
|
obs_sceneitem_set_crop(item, &crop);
|
2024-09-03 16:29:30 +02:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
obs_sceneitem_defer_update_end(item);
|
2024-09-03 16:29:30 +02:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
return true;
|
|
|
|
|
}
|
2024-09-03 16:29:30 +02:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
void OBSBasic::on_actionResetTransform_triggered()
|
|
|
|
|
{
|
|
|
|
|
OBSScene scene = GetCurrentScene();
|
2024-09-03 16:29:30 +02:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
OBSDataAutoRelease wrapper = obs_scene_save_transform_states(scene, false);
|
|
|
|
|
obs_scene_enum_items(scene, reset_tr, nullptr);
|
|
|
|
|
OBSDataAutoRelease rwrapper = obs_scene_save_transform_states(scene, false);
|
2024-09-03 16:29:30 +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.Reset").arg(obs_source_get_name(obs_scene_get_source(scene))),
|
|
|
|
|
undo_redo, undo_redo, undo_data, redo_data);
|
2024-09-03 16:29:30 +02:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
obs_scene_enum_items(GetCurrentScene(), reset_tr, nullptr);
|
|
|
|
|
}
|
2024-09-03 16:29:30 +02:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
SourceTreeItem *OBSBasic::GetItemWidgetFromSceneItem(obs_sceneitem_t *sceneItem)
|
|
|
|
|
{
|
|
|
|
|
int i = 0;
|
|
|
|
|
SourceTreeItem *treeItem = ui->sources->GetItemWidget(i);
|
|
|
|
|
OBSSceneItem item = ui->sources->Get(i);
|
|
|
|
|
int64_t id = obs_sceneitem_get_id(sceneItem);
|
|
|
|
|
while (treeItem && obs_sceneitem_get_id(item) != id) {
|
|
|
|
|
i++;
|
|
|
|
|
treeItem = ui->sources->GetItemWidget(i);
|
|
|
|
|
item = ui->sources->Get(i);
|
2024-09-03 16:29:30 +02:00
|
|
|
}
|
2024-12-11 17:43:35 +01:00
|
|
|
if (treeItem)
|
|
|
|
|
return treeItem;
|
|
|
|
|
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void OBSBasic::on_actionSceneFilters_triggered()
|
|
|
|
|
{
|
|
|
|
|
OBSSource sceneSource = GetCurrentSceneSource();
|
2024-09-03 16:29:30 +02:00
|
|
|
|
2024-12-11 17:43:35 +01:00
|
|
|
if (sceneSource)
|
|
|
|
|
OpenFilters(sceneSource);
|
2024-09-03 16:29:30 +02:00
|
|
|
}
|