UI/libobs: Undo/Redo Sources and Scenes

Implements the Undo/Redo for scenes and sources, ranging from renaming,
deletion, addition. It also adds several elements to libobs that were
designed to facilitate undo/redo, and should not affect the rest of
libobs.
master
Ford Smith 2021-03-22 00:39:02 -04:00 committed by Ford Smith
parent eced5a320b
commit 60d95cb5bd
11 changed files with 564 additions and 37 deletions

View File

@ -256,7 +256,8 @@ set(obs_SOURCES
obs-proxy-style.cpp
locked-checkbox.cpp
visibility-checkbox.cpp
media-slider.cpp)
media-slider.cpp
undo-stack-obs.cpp)
set(obs_HEADERS
${obs_PLATFORM_HEADERS}
@ -327,7 +328,8 @@ set(obs_HEADERS
log-viewer.hpp
obs-proxy-style.hpp
obs-proxy-style.hpp
media-slider.hpp)
media-slider.hpp
undo-stack-obs.hpp)
set(obs_importers_HEADERS
importers/importers.hpp)

View File

@ -586,6 +586,9 @@
<string>Paste.Filters</string>
</property>
</action>
<addaction name="actionMainUndo"/>
<addaction name="actionMainRedo"/>
<addaction name="separator"/>
<addaction name="actionCopySource"/>
<addaction name="actionPasteRef"/>
<addaction name="actionPasteDup"/>
@ -599,6 +602,7 @@
<addaction name="actionLockPreview"/>
<addaction name="separator"/>
<addaction name="actionAdvAudioProperties"/>
<addaction name="separator"/>
</widget>
<widget class="QMenu" name="profileMenu">
<property name="title">
@ -2034,7 +2038,23 @@
<string>Basic.MainMenu.View.ContextBar</string>
</property>
</action>
</widget>
<action name="actionMainUndo">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Undo</string>
</property>
</action>
<action name="actionMainRedo">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Redo</string>
</property>
</action>
</widget>
<customwidgets>
<customwidget>
<class>OBSBasicPreview</class>

View File

@ -403,6 +403,34 @@ void SourceTreeItem::ExitEditMode(bool save)
/* rename */
SignalBlocker sourcesSignalBlocker(this);
std::string prevName(obs_source_get_name(source));
std::string scene_name =
obs_source_get_name(main->GetCurrentSceneSource());
auto undo = [scene_name, prevName, main](const std::string &data) {
obs_source_t *source = obs_get_source_by_name(data.c_str());
obs_source_set_name(source, prevName.c_str());
obs_source_release(source);
obs_source_t *scene_source =
obs_get_source_by_name(scene_name.c_str());
main->SetCurrentScene(scene_source);
obs_source_release(scene_source);
};
auto redo = [scene_name, main, newName](const std::string &data) {
obs_source_t *source = obs_get_source_by_name(data.c_str());
obs_source_set_name(source, newName.c_str());
obs_source_release(source);
obs_source_t *scene_source =
obs_get_source_by_name(scene_name.c_str());
main->SetCurrentScene(scene_source);
obs_source_release(scene_source);
};
main->undo_s.add_action(QTStr("Undo.Rename").arg(newName.c_str()), undo,
redo, newName, prevName, NULL);
obs_source_set_name(source, newName.c_str());
label->setText(QT_UTF8(newName.c_str()));
}

View File

@ -17,7 +17,10 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
#include <cstddef>
#include <ctime>
#include <obs-data.h>
#include <obs.h>
#include <obs.hpp>
#include <QGuiApplication>
#include <QMessageBox>
@ -58,6 +61,7 @@
#include "remote-text.hpp"
#include "ui-validation.hpp"
#include "media-controls.hpp"
#include "undo-stack-obs.hpp"
#include <fstream>
#include <sstream>
@ -203,7 +207,7 @@ extern void RegisterTwitchAuth();
extern void RegisterRestreamAuth();
OBSBasic::OBSBasic(QWidget *parent)
: OBSMainWindow(parent), ui(new Ui::OBSBasic)
: OBSMainWindow(parent), undo_s(ui), ui(new Ui::OBSBasic)
{
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
qRegisterMetaTypeStreamOperators<SignalContainer<OBSScene>>(
@ -366,6 +370,16 @@ OBSBasic::OBSBasic(QWidget *parent)
assignDockToggle(ui->controlsDock, ui->toggleControls);
assignDockToggle(statsDock, ui->toggleStats);
// Register shortcuts for Undo/Redo
ui->actionMainUndo->setShortcut(Qt::CTRL + Qt::Key_Z);
QList<QKeySequence> shrt;
shrt << QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_Z)
<< QKeySequence(Qt::CTRL + Qt::Key_Y);
ui->actionMainRedo->setShortcuts(shrt);
ui->actionMainUndo->setShortcutContext(Qt::ApplicationShortcut);
ui->actionMainRedo->setShortcutContext(Qt::ApplicationShortcut);
//hide all docking panes
ui->toggleScenes->setChecked(false);
ui->toggleSources->setChecked(false);
@ -3622,6 +3636,33 @@ void OBSBasic::DuplicateSelectedScene()
OBS_SCENE_DUP_REFS);
source = obs_scene_get_source(scene);
SetCurrentScene(source, true);
auto undo = [](const std::string &data) {
obs_source_t *source =
obs_get_source_by_name(data.c_str());
obs_source_remove(source);
obs_source_release(source);
};
auto redo = [this, name](const std::string &data) {
obs_source_t *source =
obs_get_source_by_name(data.c_str());
obs_scene_t *scene = obs_scene_from_source(source);
obs_source_release(source);
scene = obs_scene_duplicate(scene, name.c_str(),
OBS_SCENE_DUP_REFS);
source = obs_scene_get_source(scene);
SetCurrentScene(source, true);
obs_scene_release(scene);
};
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)),
NULL);
obs_scene_release(scene);
break;
@ -3631,15 +3672,107 @@ void OBSBasic::DuplicateSelectedScene()
void OBSBasic::RemoveSelectedScene()
{
OBSScene scene = GetCurrentScene();
if (scene) {
obs_source_t *source = obs_scene_get_source(scene);
if (QueryRemoveSource(source)) {
obs_source_remove(source);
obs_source_t *source = obs_scene_get_source(scene);
if (api)
api->on_event(
OBS_FRONTEND_EVENT_SCENE_LIST_CHANGED);
}
OBSSource curProgramScene = OBSGetStrongRef(programScene);
if (source && QueryRemoveSource(source)) {
vector<std::string> item_ids;
obs_data_t *wrapper = obs_save_source(source);
obs_data_array_t *arr = obs_data_array_create();
struct wrap {
obs_data_array_t *arr;
vector<std::string> &items;
};
wrap passthrough = {arr, item_ids};
obs_scene_enum_items(
scene,
[](obs_scene_t *, obs_sceneitem_t *item,
void *vp_wrap) {
wrap *passthrough = (wrap *)vp_wrap;
passthrough->items.push_back(obs_source_get_name(
obs_sceneitem_get_source(item)));
obs_data_array_t *arr = passthrough->arr;
obs_sceneitem_save(item, arr);
obs_source_addref(
obs_sceneitem_get_source(item));
return true;
},
(void *)&passthrough);
obs_data_array_t *list_order = SaveSceneListOrder();
obs_data_set_array(wrapper, "arr", arr);
obs_data_set_array(wrapper, "list_order", list_order);
obs_data_set_string(wrapper, "name",
obs_source_get_name(source));
auto d = [item_ids](bool remove_ref) {
for (auto &item : item_ids) {
obs_source_t *source =
obs_get_source_by_name(item.c_str());
blog(LOG_INFO, "%s", item.c_str());
if (remove_ref) {
obs_source_release(source);
obs_source_release(source);
}
}
};
auto undo = [this, d](const std::string &data) {
obs_data_t *dat =
obs_data_create_from_json(data.c_str());
obs_source_release(obs_load_source(dat));
obs_data_array_t *arr = obs_data_get_array(dat, "arr");
obs_data_array_t *list_order =
obs_data_get_array(dat, "list_order");
const char *sname = obs_data_get_string(dat, "name");
obs_source_t *source = obs_get_source_by_name(sname);
obs_scene_t *scene = obs_scene_from_source(source);
obs_sceneitems_add(scene, arr);
LoadSceneListOrder(list_order);
SetCurrentScene(source);
obs_data_release(dat);
obs_data_array_release(arr);
obs_data_array_release(list_order);
obs_source_release(source);
d(true);
};
obs_data_t *rwrapper = obs_data_create();
obs_data_set_string(rwrapper, "name",
obs_source_get_name(source));
auto redo = [d](const std::string &data) {
obs_data_t *dat =
obs_data_create_from_json(data.c_str());
obs_source_t *source = obs_get_source_by_name(
obs_data_get_string(dat, "name"));
obs_source_remove(source);
obs_source_release(source);
obs_data_release(dat);
d(false);
};
std::string undo_data = obs_data_get_json(wrapper);
std::string redo_data = obs_data_get_json(wrapper);
undo_s.add_action(
QTStr("Undo.Delete").arg(obs_source_get_name(source)),
undo, redo, undo_data, redo_data, [d](bool undo) {
if (undo) {
d(true);
}
});
obs_source_remove(source);
obs_data_release(wrapper);
obs_data_release(rwrapper);
obs_data_array_release(arr);
obs_data_array_release(list_order);
if (api)
api->on_event(OBS_FRONTEND_EVENT_SCENE_LIST_CHANGED);
}
}
@ -4330,6 +4463,7 @@ void OBSBasic::closeEvent(QCloseEvent *event)
/* Clear all scene data (dialogs, widgets, widget sub-items, scenes,
* sources, etc) so that all references are released before shutdown */
undo_s.release();
ClearSceneData();
App()->quit();
@ -4686,20 +4820,34 @@ void OBSBasic::on_actionAddScene_triggered()
return;
}
auto undo_fn = [](const std::string &data) {
obs_source_t *t = obs_get_source_by_name(data.c_str());
if (t) {
obs_source_release(t);
obs_source_remove(t);
}
};
auto redo_fn = [this](const std::string &data) {
obs_scene_t *scene = obs_scene_create(data.c_str());
obs_source_t *source = obs_scene_get_source(scene);
SetCurrentScene(source);
obs_scene_release(scene);
};
undo_s.add_action(QTStr("Undo.Add").arg(QString(name.c_str())),
undo_fn, redo_fn, name, name, NULL);
obs_scene_t *scene = obs_scene_create(name.c_str());
source = obs_scene_get_source(scene);
SetCurrentScene(source);
RefreshSources(scene);
obs_scene_release(scene);
}
}
void OBSBasic::on_actionRemoveScene_triggered()
{
OBSScene scene = GetCurrentScene();
obs_source_t *source = obs_scene_get_source(scene);
if (source && QueryRemoveSource(source))
obs_source_remove(source);
RemoveSelectedScene();
}
void OBSBasic::ChangeSceneIndex(bool relative, int offset, int invalidIdx)
@ -5109,10 +5257,11 @@ void OBSBasic::on_scenes_itemDoubleClicked(QListWidgetItem *witem)
void OBSBasic::AddSource(const char *id)
{
if (id && *id) {
OBSBasicSourceSelect sourceSelect(this, id);
OBSBasicSourceSelect sourceSelect(this, id, undo_s);
sourceSelect.exec();
if (sourceSelect.newSource && strcmp(id, "group") != 0)
if (sourceSelect.newSource && strcmp(id, "group") != 0) {
CreatePropertiesWindow(sourceSelect.newSource);
}
}
}
@ -5253,9 +5402,11 @@ void OBSBasic::on_actionRemoveSource_triggered()
if (!items.size())
return;
auto removeMultiple = [this](size_t count) {
bool confirmed = false;
if (items.size() > 1) {
QString text = QTStr("ConfirmRemove.TextMultiple")
.arg(QString::number(count));
.arg(QString::number(items.size()));
QMessageBox remove_items(this);
remove_items.setText(text);
@ -5267,21 +5418,139 @@ void OBSBasic::on_actionRemoveSource_triggered()
remove_items.setWindowTitle(QTStr("ConfirmRemove.Title"));
remove_items.exec();
return Yes == remove_items.clickedButton();
};
if (items.size() == 1) {
confirmed = remove_items.clickedButton();
} else {
OBSSceneItem &item = items[0];
obs_source_t *source = obs_sceneitem_get_source(item);
if (source && QueryRemoveSource(source))
obs_sceneitem_remove(item);
} else {
if (removeMultiple(items.size())) {
for (auto &item : items)
obs_sceneitem_remove(item);
}
confirmed = true;
}
if (!confirmed)
return;
struct source_save {
std::string name;
std::string scene_name;
int pos;
bool in_group = false;
int64_t group_id;
};
vector<source_save> item_save;
obs_data_t *wrapper = obs_data_create();
obs_data_array_t *data = obs_data_array_create();
for (const auto &item : items) {
obs_sceneitem_save(item, data);
obs_source_t *source = obs_sceneitem_get_source(item);
obs_source_addref(source);
obs_source_set_hidden(source, true);
obs_sceneitem_t *grp =
obs_sceneitem_get_group(GetCurrentScene(), item);
obs_scene_t *scene = obs_sceneitem_get_scene(item);
source_save save = {
obs_source_get_name(source),
obs_source_get_name(obs_scene_get_source(scene)),
obs_sceneitem_get_order_position(item),
grp ? true : false, obs_sceneitem_get_id(grp)};
item_save.push_back(save);
}
obs_scene_t *scene = GetCurrentScene();
const char *name = obs_source_get_name(obs_scene_get_source(scene));
obs_data_set_array(wrapper, "data_array", data);
obs_data_set_string(wrapper, "name", name);
std::string undo_data(obs_data_get_json(wrapper));
auto undo_fn = [this, item_save](const std::string &data) {
obs_data_t *dat = obs_data_create_from_json(data.c_str());
obs_data_array_t *sources_data =
obs_data_get_array(dat, "data_array");
const char *name = obs_data_get_string(dat, "name");
obs_source_t *src = obs_get_source_by_name(name);
obs_scene_t *scene = obs_scene_from_source(src);
obs_sceneitems_add(scene, sources_data);
SetCurrentScene(scene);
for (const auto &save : item_save) {
obs_source_t *source =
obs_get_source_by_name(save.name.c_str());
obs_source_set_hidden(source, false);
if (save.in_group) {
obs_sceneitem_t *grp =
obs_scene_find_sceneitem_by_id(
scene, save.group_id);
obs_sceneitem_t *item =
obs_scene_sceneitem_from_source(scene,
source);
obs_sceneitem_group_add_item(grp, item);
obs_sceneitem_set_order_position(item,
save.pos);
obs_sceneitem_release(item);
}
obs_source_release(source);
obs_source_release(source);
}
obs_source_release(src);
obs_data_array_release(sources_data);
obs_data_release(dat);
};
auto redo_fn = [item_save](const std::string &) {
for (const auto &save : item_save) {
obs_source_t *source =
obs_get_source_by_name(save.name.c_str());
obs_source_t *scene_source =
obs_get_source_by_name(save.scene_name.c_str());
obs_scene_t *scene =
obs_scene_from_source(scene_source);
if (!scene)
scene = obs_group_from_source(scene_source);
obs_sceneitem_t *item =
obs_scene_sceneitem_from_source(scene, source);
obs_sceneitem_remove(item);
obs_source_set_hidden(source, true);
obs_sceneitem_release(item);
obs_source_release(scene_source);
/* usually want to release source, but redo needs to add a reference to keep alive */
}
};
auto d = [item_save](bool is_undo) {
if (!is_undo)
return;
for (const auto &item : item_save) {
obs_source_t *source =
obs_get_source_by_name(item.name.c_str());
obs_source_release(source);
obs_source_release(source);
}
};
QString action_name;
if (items.size() > 1)
action_name = QTStr("Undo.Sources.Multi")
.arg(QString::number(items.size()));
else
action_name =
QTStr("Undo.Delete")
.arg(QString(obs_source_get_name(
obs_sceneitem_get_source(items[0]))));
undo_s.add_action(action_name, undo_fn, redo_fn, undo_data, "", d);
obs_data_array_release(data);
obs_data_release(wrapper);
for (auto &item : items)
obs_sceneitem_remove(item);
}
void OBSBasic::on_actionInteract_triggered()
@ -5519,6 +5788,27 @@ static void RenameListItem(OBSBasic *parent, QListWidget *listWidget,
obs_source_release(foundSource);
} else {
auto undo = [prev = std::string(prevName)](
const std::string &data) {
obs_source_t *source =
obs_get_source_by_name(data.c_str());
obs_source_set_name(source, prev.c_str());
obs_source_release(source);
};
auto redo = [name](const std::string &data) {
obs_source_t *source =
obs_get_source_by_name(data.c_str());
obs_source_set_name(source, name.c_str());
obs_source_release(source);
};
std::string undo_data(name);
std::string redo_data(prevName);
parent->undo_s.add_action(
QTStr("Undo.Rename").arg(name.c_str()), undo, redo,
undo_data, redo_data, NULL);
listItem->setText(QT_UTF8(name.c_str()));
obs_source_set_name(source, name.c_str());
}
@ -7762,6 +8052,16 @@ bool OBSBasic::sysTrayMinimizeToTray()
"SysTrayMinimizeToTray");
}
void OBSBasic::on_actionMainUndo_triggered()
{
undo_s.undo();
}
void OBSBasic::on_actionMainRedo_triggered()
{
undo_s.redo();
}
void OBSBasic::on_actionCopySource_triggered()
{
copyStrings.clear();

View File

@ -36,6 +36,7 @@
#include "window-basic-about.hpp"
#include "auth-base.hpp"
#include "log-viewer.hpp"
#include "undo-stack-obs.hpp"
#include <obs-frontend-internal.hpp>
@ -166,6 +167,7 @@ class OBSBasic : public OBSMainWindow {
friend class ExtraBrowsersDelegate;
friend class DeviceCaptureToolbar;
friend class DeviceToolbarPropertiesThread;
friend class OBSBasicSourceSelect;
friend struct BasicOutputHandler;
friend struct OBSStudioAPI;
@ -608,6 +610,10 @@ public slots:
void UnpauseRecording();
private slots:
void on_actionMainUndo_triggered();
void on_actionMainRedo_triggered();
void AddSceneItem(OBSSceneItem item);
void AddScene(OBSSource source);
void RemoveScene(OBSSource source);
@ -740,6 +746,7 @@ private:
OBSSource prevFTBSource = nullptr;
public:
undo_stack undo_s;
OBSSource GetProgramSource();
OBSScene GetCurrentScene();

View File

@ -28,6 +28,9 @@ struct AddSourceData {
bool OBSBasicSourceSelect::EnumSources(void *data, obs_source_t *source)
{
if (obs_source_is_hidden(source))
return false;
OBSBasicSourceSelect *window =
static_cast<OBSBasicSourceSelect *>(data);
const char *name = obs_source_get_name(source);
@ -179,7 +182,7 @@ bool AddNew(QWidget *parent, const char *id, const char *name,
return false;
obs_source_t *source = obs_get_source_by_name(name);
if (source) {
if (source && parent) {
OBSMessageBox::information(parent, QTStr("NameExists.Title"),
QTStr("NameExists.Text"));
@ -236,6 +239,63 @@ void OBSBasicSourceSelect::on_buttonBox_accepted()
if (!AddNew(this, id, QT_TO_UTF8(ui->sourceName->text()),
visible, newSource))
return;
OBSBasic *main =
reinterpret_cast<OBSBasic *>(App()->GetMainWindow());
std::string scene_name =
obs_source_get_name(main->GetCurrentSceneSource());
auto undo = [scene_name, main](const std::string &data) {
obs_source_t *source =
obs_get_source_by_name(data.c_str());
obs_source_release(source);
obs_source_remove(source);
obs_source_t *scene_source =
obs_get_source_by_name(scene_name.c_str());
main->SetCurrentScene(scene_source);
obs_source_release(scene_source);
main->RefreshSources(main->GetCurrentScene());
};
obs_data_t *wrapper = obs_data_create();
obs_data_set_string(wrapper, "id", id);
obs_sceneitem_t *item = obs_scene_sceneitem_from_source(
main->GetCurrentScene(), newSource);
obs_data_set_int(wrapper, "item_id",
obs_sceneitem_get_id(item));
obs_data_set_string(
wrapper, "name",
ui->sourceName->text().toUtf8().constData());
obs_data_set_bool(wrapper, "visible", visible);
auto redo = [scene_name, main](const std::string &data) {
obs_data_t *dat =
obs_data_create_from_json(data.c_str());
OBSSource source;
AddNew(NULL, obs_data_get_string(dat, "id"),
obs_data_get_string(dat, "name"),
obs_data_get_bool(dat, "visible"), source);
obs_sceneitem_t *item = obs_scene_sceneitem_from_source(
main->GetCurrentScene(), source);
obs_sceneitem_set_id(item, (int64_t)obs_data_get_int(
dat, "item_id"));
obs_source_t *scene_source =
obs_get_source_by_name(scene_name.c_str());
main->SetCurrentScene(scene_source);
obs_source_release(scene_source);
main->RefreshSources(main->GetCurrentScene());
obs_data_release(dat);
obs_sceneitem_release(item);
};
undo_s.add_action(QTStr("Undo.Add").arg(ui->sourceName->text()),
undo, redo,
std::string(obs_source_get_name(newSource)),
std::string(obs_data_get_json(wrapper)),
NULL);
obs_data_release(wrapper);
obs_sceneitem_release(item);
}
done(DialogCode::Accepted);
@ -261,8 +321,12 @@ template<typename T> static inline T GetOBSRef(QListWidgetItem *item)
return item->data(static_cast<int>(QtDataRole::OBSRef)).value<T>();
}
OBSBasicSourceSelect::OBSBasicSourceSelect(OBSBasic *parent, const char *id_)
: QDialog(parent), ui(new Ui::OBSBasicSourceSelect), id(id_)
OBSBasicSourceSelect::OBSBasicSourceSelect(OBSBasic *parent, const char *id_,
undo_stack &undo_s)
: QDialog(parent),
ui(new Ui::OBSBasicSourceSelect),
id(id_),
undo_s(undo_s)
{
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);

View File

@ -21,6 +21,7 @@
#include <memory>
#include "ui_OBSBasicSourceSelect.h"
#include "undo-stack-obs.hpp"
class OBSBasic;
@ -30,6 +31,7 @@ class OBSBasicSourceSelect : public QDialog {
private:
std::unique_ptr<Ui::OBSBasicSourceSelect> ui;
const char *id;
undo_stack &undo_s;
static bool EnumSources(void *data, obs_source_t *source);
static bool EnumGroups(void *data, obs_source_t *source);
@ -45,7 +47,8 @@ private slots:
void SourceRemoved(OBSSource source);
public:
OBSBasicSourceSelect(OBSBasic *parent, const char *id);
OBSBasicSourceSelect(OBSBasic *parent, const char *id,
undo_stack &undo_s);
OBSSource newSource;

View File

@ -621,6 +621,9 @@ struct obs_source {
* to handle things but it's the best option) */
bool removed;
/* used to indicate if the source should show up when queried for user ui */
bool temp_removed;
bool active;
bool showing;

View File

@ -1477,6 +1477,33 @@ obs_sceneitem_t *obs_scene_find_source_recursive(obs_scene_t *scene,
return item;
}
struct sceneitem_check {
obs_source_t *source_in;
obs_sceneitem_t *item_out;
};
bool check_sceneitem_exists(obs_scene_t *scene, obs_sceneitem_t *item,
void *vp_check)
{
UNUSED_PARAMETER(scene);
struct sceneitem_check *check = (struct sceneitem_check *)vp_check;
if (obs_sceneitem_get_source(item) == check->source_in) {
check->item_out = item;
obs_sceneitem_addref(item);
return false;
}
return true;
}
obs_sceneitem_t *obs_scene_sceneitem_from_source(obs_scene_t *scene,
obs_source_t *source)
{
struct sceneitem_check check = {source, NULL};
obs_scene_enum_items(scene, check_sceneitem_exists, (void *)&check);
return check.item_out;
}
obs_sceneitem_t *obs_scene_find_sceneitem_by_id(obs_scene_t *scene, int64_t id)
{
struct obs_scene_item *item;
@ -1830,6 +1857,22 @@ void obs_sceneitem_remove(obs_sceneitem_t *item)
obs_sceneitem_release(item);
}
void obs_sceneitem_save(obs_sceneitem_t *item, obs_data_array_t *arr)
{
scene_save_item(arr, item, NULL);
}
void sceneitem_restore(obs_data_t *data, void *vp)
{
obs_scene_t *scene = (obs_scene_t *)vp;
scene_load_item(scene, data);
}
void obs_sceneitems_add(obs_scene_t *scene, obs_data_array_t *data)
{
obs_data_array_enum(data, sceneitem_restore, scene);
}
obs_scene_t *obs_sceneitem_get_scene(const obs_sceneitem_t *item)
{
return item ? item->parent : NULL;
@ -1977,6 +2020,24 @@ void obs_sceneitem_set_order(obs_sceneitem_t *item,
obs_scene_release(scene);
}
int obs_sceneitem_get_order_position(obs_sceneitem_t *item)
{
struct obs_scene *scene = item->parent;
struct obs_scene_item *next = scene->first_item;
full_lock(scene);
int index = 0;
while (next && next != item) {
next = next->next;
++index;
}
full_unlock(scene);
return index;
}
void obs_sceneitem_set_order_position(obs_sceneitem_t *item, int position)
{
if (!item)
@ -2385,6 +2446,11 @@ int64_t obs_sceneitem_get_id(const obs_sceneitem_t *item)
return item->id;
}
void obs_sceneitem_set_id(obs_sceneitem_t *item, int64_t id)
{
item->id = id;
}
obs_data_t *obs_sceneitem_get_private_settings(obs_sceneitem_t *item)
{
if (!obs_ptr_valid(item, "obs_sceneitem_get_private_settings"))

View File

@ -929,8 +929,9 @@ void obs_source_update(obs_source_t *source, obs_data_t *settings)
if (!obs_source_valid(source, "obs_source_update"))
return;
if (settings)
if (settings) {
obs_data_apply(source->context.settings, settings);
}
if (source->info.output_flags & OBS_SOURCE_VIDEO) {
os_atomic_inc_long(&source->defer_update_count);
@ -4283,6 +4284,16 @@ void obs_source_enum_filters(obs_source_t *source,
pthread_mutex_unlock(&source->filter_mutex);
}
void obs_source_set_hidden(obs_source_t *source, bool hidden)
{
source->temp_removed = hidden;
}
bool obs_source_is_hidden(obs_source_t *source)
{
return source->temp_removed;
}
obs_source_t *obs_source_get_filter_by_name(obs_source_t *source,
const char *name)
{

View File

@ -898,6 +898,14 @@ EXPORT void obs_source_remove(obs_source_t *source);
/** Returns true if the source should be released */
EXPORT bool obs_source_removed(const obs_source_t *source);
/** The 'hidden' flag is not the same as a sceneitem's visibility. It is a
* property the determines if it can be found through searches. **/
/** Simply sets a 'hidden' flag when the source is still alive but shouldn't be found */
EXPORT void obs_source_set_hidden(obs_source_t *source, bool hidden);
/** Returns the current 'hidden' state on the source */
EXPORT bool obs_source_is_hidden(obs_source_t *source);
/** Returns capability flags of a source */
EXPORT uint32_t obs_source_get_output_flags(const obs_source_t *source);
@ -1578,6 +1586,21 @@ EXPORT void obs_sceneitem_release(obs_sceneitem_t *item);
/** Removes a scene item. */
EXPORT void obs_sceneitem_remove(obs_sceneitem_t *item);
/** Adds a scene item. */
EXPORT void obs_sceneitems_add(obs_scene_t *scene, obs_data_array_t *data);
/** Saves Sceneitem into an array, arr **/
EXPORT void obs_sceneitem_save(obs_sceneitem_t *item, obs_data_array_t *arr);
/** Set the ID of a sceneitem */
EXPORT void obs_sceneitem_set_id(obs_sceneitem_t *sceneitem, int64_t id);
/** Tries to find the sceneitem of the source in a given scene. Returns NULL if not found */
EXPORT obs_sceneitem_t *obs_scene_sceneitem_from_source(obs_scene_t *scene,
obs_source_t *source);
/** Gets a sceneitem's order in its scene */
EXPORT int obs_sceneitem_get_order_position(obs_sceneitem_t *item);
/** Gets the scene parent associated with the scene item. */
EXPORT obs_scene_t *obs_sceneitem_get_scene(const obs_sceneitem_t *item);