UI: Undo/Redo Transformations

Implements undo/redo for transformations of sources, both through
preview and the transformations properties.
This commit is contained in:
Ford Smith 2021-03-22 01:14:37 -04:00 committed by Ford Smith
parent 60d95cb5bd
commit 3a620c485e
8 changed files with 426 additions and 2 deletions

View File

@ -925,8 +925,6 @@ void OBSBasic::LogScenes()
void OBSBasic::Load(const char *file) void OBSBasic::Load(const char *file)
{ {
disableSaving++;
obs_data_t *data = obs_data_create_from_json_file_safe(file, "bak"); obs_data_t *data = obs_data_create_from_json_file_safe(file, "bak");
if (!data) { if (!data) {
disableSaving--; disableSaving--;
@ -7024,8 +7022,23 @@ void OBSBasic::on_actionCopyTransform_triggered()
ui->actionPasteTransform->setEnabled(true); ui->actionPasteTransform->setEnabled(true);
} }
void undo_redo(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, "scene_name"));
reinterpret_cast<OBSBasic *>(App()->GetMainWindow())
->SetCurrentScene(source);
obs_source_release(source);
obs_data_release(dat);
obs_scene_load_transform_states(data.c_str());
}
void OBSBasic::on_actionPasteTransform_triggered() void OBSBasic::on_actionPasteTransform_triggered()
{ {
obs_data_t *wrapper =
obs_scene_save_transform_states(GetCurrentScene(), false);
auto func = [](obs_scene_t *scene, obs_sceneitem_t *item, void *param) { auto func = [](obs_scene_t *scene, obs_sceneitem_t *item, void *param) {
if (!obs_sceneitem_selected(item)) if (!obs_sceneitem_selected(item))
return true; return true;
@ -7041,6 +7054,19 @@ void OBSBasic::on_actionPasteTransform_triggered()
}; };
obs_scene_enum_items(GetCurrentScene(), func, nullptr); obs_scene_enum_items(GetCurrentScene(), func, nullptr);
obs_data_t *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.Paste")
.arg(obs_source_get_name(GetCurrentSceneSource())),
undo_redo, undo_redo, undo_data, redo_data, NULL);
obs_data_release(wrapper);
obs_data_release(rwrapper);
} }
static bool reset_tr(obs_scene_t *scene, obs_sceneitem_t *item, void *param) static bool reset_tr(obs_scene_t *scene, obs_sceneitem_t *item, void *param)
@ -7076,6 +7102,22 @@ static bool reset_tr(obs_scene_t *scene, obs_sceneitem_t *item, void *param)
void OBSBasic::on_actionResetTransform_triggered() void OBSBasic::on_actionResetTransform_triggered()
{ {
obs_scene_t *scene = GetCurrentScene();
obs_data_t *wrapper = obs_scene_save_transform_states(scene, false);
obs_scene_enum_items(scene, reset_tr, nullptr);
obs_data_t *rwrapper = obs_scene_save_transform_states(scene, 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.Reset")
.arg(obs_source_get_name(obs_scene_get_source(scene))),
undo_redo, undo_redo, undo_data, redo_data, NULL);
obs_data_release(wrapper);
obs_data_release(rwrapper);
obs_scene_enum_items(GetCurrentScene(), reset_tr, nullptr); obs_scene_enum_items(GetCurrentScene(), reset_tr, nullptr);
} }
@ -7153,19 +7195,61 @@ static bool RotateSelectedSources(obs_scene_t *scene, obs_sceneitem_t *item,
void OBSBasic::on_actionRotate90CW_triggered() void OBSBasic::on_actionRotate90CW_triggered()
{ {
float f90CW = 90.0f; float f90CW = 90.0f;
obs_data_t *wrapper =
obs_scene_save_transform_states(GetCurrentScene(), false);
obs_scene_enum_items(GetCurrentScene(), RotateSelectedSources, &f90CW); obs_scene_enum_items(GetCurrentScene(), RotateSelectedSources, &f90CW);
obs_data_t *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.Rotate")
.arg(obs_source_get_name(obs_scene_get_source(
GetCurrentScene()))),
undo_redo, undo_redo, undo_data, redo_data, NULL);
obs_data_release(wrapper);
obs_data_release(rwrapper);
} }
void OBSBasic::on_actionRotate90CCW_triggered() void OBSBasic::on_actionRotate90CCW_triggered()
{ {
float f90CCW = -90.0f; float f90CCW = -90.0f;
obs_data_t *wrapper =
obs_scene_save_transform_states(GetCurrentScene(), false);
obs_scene_enum_items(GetCurrentScene(), RotateSelectedSources, &f90CCW); obs_scene_enum_items(GetCurrentScene(), RotateSelectedSources, &f90CCW);
obs_data_t *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.Rotate")
.arg(obs_source_get_name(obs_scene_get_source(
GetCurrentScene()))),
undo_redo, undo_redo, undo_data, redo_data, NULL);
obs_data_release(wrapper);
obs_data_release(rwrapper);
} }
void OBSBasic::on_actionRotate180_triggered() void OBSBasic::on_actionRotate180_triggered()
{ {
float f180 = 180.0f; float f180 = 180.0f;
obs_data_t *wrapper =
obs_scene_save_transform_states(GetCurrentScene(), false);
obs_scene_enum_items(GetCurrentScene(), RotateSelectedSources, &f180); obs_scene_enum_items(GetCurrentScene(), RotateSelectedSources, &f180);
obs_data_t *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.Rotate")
.arg(obs_source_get_name(obs_scene_get_source(
GetCurrentScene()))),
undo_redo, undo_redo, undo_data, redo_data, NULL);
obs_data_release(wrapper);
obs_data_release(rwrapper);
} }
static bool MultiplySelectedItemScale(obs_scene_t *scene, obs_sceneitem_t *item, static bool MultiplySelectedItemScale(obs_scene_t *scene, obs_sceneitem_t *item,
@ -7200,16 +7284,44 @@ void OBSBasic::on_actionFlipHorizontal_triggered()
{ {
vec2 scale; vec2 scale;
vec2_set(&scale, -1.0f, 1.0f); vec2_set(&scale, -1.0f, 1.0f);
obs_data_t *wrapper =
obs_scene_save_transform_states(GetCurrentScene(), false);
obs_scene_enum_items(GetCurrentScene(), MultiplySelectedItemScale, obs_scene_enum_items(GetCurrentScene(), MultiplySelectedItemScale,
&scale); &scale);
obs_data_t *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.HFlip")
.arg(obs_source_get_name(obs_scene_get_source(
GetCurrentScene()))),
undo_redo, undo_redo, undo_data, redo_data, NULL);
obs_data_release(wrapper);
obs_data_release(rwrapper);
} }
void OBSBasic::on_actionFlipVertical_triggered() void OBSBasic::on_actionFlipVertical_triggered()
{ {
vec2 scale; vec2 scale;
vec2_set(&scale, 1.0f, -1.0f); vec2_set(&scale, 1.0f, -1.0f);
obs_data_t *wrapper =
obs_scene_save_transform_states(GetCurrentScene(), false);
obs_scene_enum_items(GetCurrentScene(), MultiplySelectedItemScale, obs_scene_enum_items(GetCurrentScene(), MultiplySelectedItemScale,
&scale); &scale);
obs_data_t *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.VFlip")
.arg(obs_source_get_name(obs_scene_get_source(
GetCurrentScene()))),
undo_redo, undo_redo, undo_data, redo_data, NULL);
obs_data_release(wrapper);
obs_data_release(rwrapper);
} }
static bool CenterAlignSelectedItems(obs_scene_t *scene, obs_sceneitem_t *item, static bool CenterAlignSelectedItems(obs_scene_t *scene, obs_sceneitem_t *item,
@ -7249,15 +7361,43 @@ static bool CenterAlignSelectedItems(obs_scene_t *scene, obs_sceneitem_t *item,
void OBSBasic::on_actionFitToScreen_triggered() void OBSBasic::on_actionFitToScreen_triggered()
{ {
obs_bounds_type boundsType = OBS_BOUNDS_SCALE_INNER; obs_bounds_type boundsType = OBS_BOUNDS_SCALE_INNER;
obs_data_t *wrapper =
obs_scene_save_transform_states(GetCurrentScene(), false);
obs_scene_enum_items(GetCurrentScene(), CenterAlignSelectedItems, obs_scene_enum_items(GetCurrentScene(), CenterAlignSelectedItems,
&boundsType); &boundsType);
obs_data_t *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.FitToScreen")
.arg(obs_source_get_name(obs_scene_get_source(
GetCurrentScene()))),
undo_redo, undo_redo, undo_data, redo_data, NULL);
obs_data_release(wrapper);
obs_data_release(rwrapper);
} }
void OBSBasic::on_actionStretchToScreen_triggered() void OBSBasic::on_actionStretchToScreen_triggered()
{ {
obs_bounds_type boundsType = OBS_BOUNDS_STRETCH; obs_bounds_type boundsType = OBS_BOUNDS_STRETCH;
obs_data_t *wrapper =
obs_scene_save_transform_states(GetCurrentScene(), false);
obs_scene_enum_items(GetCurrentScene(), CenterAlignSelectedItems, obs_scene_enum_items(GetCurrentScene(), CenterAlignSelectedItems,
&boundsType); &boundsType);
obs_data_t *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.StretchToScreen")
.arg(obs_source_get_name(obs_scene_get_source(
GetCurrentScene()))),
undo_redo, undo_redo, undo_data, redo_data, NULL);
obs_data_release(wrapper);
obs_data_release(rwrapper);
} }
enum class CenterType { enum class CenterType {
@ -7318,19 +7458,61 @@ static bool center_to_scene(obs_scene_t *, obs_sceneitem_t *item, void *param)
void OBSBasic::on_actionCenterToScreen_triggered() void OBSBasic::on_actionCenterToScreen_triggered()
{ {
CenterType centerType = CenterType::Scene; CenterType centerType = CenterType::Scene;
obs_data_t *wrapper =
obs_scene_save_transform_states(GetCurrentScene(), false);
obs_scene_enum_items(GetCurrentScene(), center_to_scene, &centerType); obs_scene_enum_items(GetCurrentScene(), center_to_scene, &centerType);
obs_data_t *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.Center")
.arg(obs_source_get_name(obs_scene_get_source(
GetCurrentScene()))),
undo_redo, undo_redo, undo_data, redo_data, NULL);
obs_data_release(wrapper);
obs_data_release(rwrapper);
} }
void OBSBasic::on_actionVerticalCenter_triggered() void OBSBasic::on_actionVerticalCenter_triggered()
{ {
CenterType centerType = CenterType::Vertical; CenterType centerType = CenterType::Vertical;
obs_data_t *wrapper =
obs_scene_save_transform_states(GetCurrentScene(), false);
obs_scene_enum_items(GetCurrentScene(), center_to_scene, &centerType); obs_scene_enum_items(GetCurrentScene(), center_to_scene, &centerType);
obs_data_t *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, NULL);
obs_data_release(wrapper);
obs_data_release(rwrapper);
} }
void OBSBasic::on_actionHorizontalCenter_triggered() void OBSBasic::on_actionHorizontalCenter_triggered()
{ {
CenterType centerType = CenterType::Horizontal; CenterType centerType = CenterType::Horizontal;
obs_data_t *wrapper =
obs_scene_save_transform_states(GetCurrentScene(), false);
obs_scene_enum_items(GetCurrentScene(), center_to_scene, &centerType); obs_scene_enum_items(GetCurrentScene(), center_to_scene, &centerType);
obs_data_t *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, NULL);
obs_data_release(wrapper);
obs_data_release(rwrapper);
} }
void OBSBasic::EnablePreviewDisplay(bool enable) void OBSBasic::EnablePreviewDisplay(bool enable)
@ -7421,6 +7603,46 @@ void OBSBasic::Nudge(int dist, MoveDir dir)
break; break;
} }
if (!recent_nudge) {
recent_nudge = true;
obs_data_t *wrapper = obs_scene_save_transform_states(
GetCurrentScene(), true);
std::string undo_data(obs_data_get_json(wrapper));
nudge_timer = new QTimer;
QObject::connect(
nudge_timer, &QTimer::timeout,
[this, &recent_nudge = recent_nudge, undo_data]() {
obs_data_t *rwrapper =
obs_scene_save_transform_states(
GetCurrentScene(), true);
std::string redo_data(
obs_data_get_json(rwrapper));
undo_s.add_action(
QTStr("Undo.Transform")
.arg(obs_source_get_name(
GetCurrentSceneSource())),
undo_redo, undo_redo, undo_data,
redo_data, NULL);
recent_nudge = false;
obs_data_release(rwrapper);
});
connect(nudge_timer, &QTimer::timeout, nudge_timer,
&QTimer::deleteLater);
nudge_timer->setSingleShot(true);
obs_data_release(wrapper);
}
if (nudge_timer) {
nudge_timer->stop();
nudge_timer->start(1000);
} else {
blog(LOG_ERROR, "No nudge timer!");
}
obs_scene_enum_items(GetCurrentScene(), nudge_callback, &offset); obs_scene_enum_items(GetCurrentScene(), nudge_callback, &offset);
} }

View File

@ -157,6 +157,7 @@ class OBSBasic : public OBSMainWindow {
friend class OBSBasicPreview; friend class OBSBasicPreview;
friend class OBSBasicStatusBar; friend class OBSBasicStatusBar;
friend class OBSBasicSourceSelect; friend class OBSBasicSourceSelect;
friend class OBSBasicTransform;
friend class OBSBasicSettings; friend class OBSBasicSettings;
friend class Auth; friend class Auth;
friend class AutoConfig; friend class AutoConfig;
@ -222,6 +223,9 @@ private:
QPointer<QTimer> cpuUsageTimer; QPointer<QTimer> cpuUsageTimer;
QPointer<QTimer> diskFullTimer; QPointer<QTimer> diskFullTimer;
QPointer<QTimer> nudge_timer;
bool recent_nudge = false;
os_cpu_usage_info_t *cpuUsageInfo = nullptr; os_cpu_usage_info_t *cpuUsageInfo = nullptr;
OBSService service; OBSService service;

View File

@ -36,6 +36,9 @@ OBSBasicPreview::~OBSBasicPreview()
gs_vertexbuffer_destroy(rectFill); gs_vertexbuffer_destroy(rectFill);
obs_leave_graphics(); obs_leave_graphics();
if (wrapper)
obs_data_release(wrapper);
} }
vec2 OBSBasicPreview::GetMouseEventPos(QMouseEvent *event) vec2 OBSBasicPreview::GetMouseEventPos(QMouseEvent *event)
@ -581,6 +584,11 @@ void OBSBasicPreview::mousePressEvent(QMouseEvent *event)
vec2_zero(&lastMoveOffset); vec2_zero(&lastMoveOffset);
mousePos = startPos; mousePos = startPos;
if (wrapper)
obs_data_release(wrapper);
wrapper =
obs_scene_save_transform_states(main->GetCurrentScene(), true);
changed = false;
} }
void OBSBasicPreview::UpdateCursor(uint32_t &flags) void OBSBasicPreview::UpdateCursor(uint32_t &flags)
@ -713,6 +721,41 @@ void OBSBasicPreview::mouseReleaseEvent(QMouseEvent *event)
hoveredPreviewItems.push_back(item); hoveredPreviewItems.push_back(item);
selectedItems.clear(); selectedItems.clear();
} }
OBSBasic *main = reinterpret_cast<OBSBasic *>(App()->GetMainWindow());
obs_data_t *rwrapper =
obs_scene_save_transform_states(main->GetCurrentScene(), false);
auto undo_redo = [](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, "scene_name"));
reinterpret_cast<OBSBasic *>(App()->GetMainWindow())
->SetCurrentScene(source);
obs_source_release(source);
obs_data_release(dat);
obs_scene_load_transform_states(data.c_str());
};
if (wrapper && rwrapper) {
std::string undo_data(obs_data_get_json(wrapper));
std::string redo_data(obs_data_get_json(rwrapper));
if (changed && undo_data.compare(redo_data) != 0)
main->undo_s.add_action(
QTStr("Undo.Transform")
.arg(obs_source_get_name(
main->GetCurrentSceneSource())),
undo_redo, undo_redo, undo_data, redo_data,
NULL);
}
if (wrapper)
obs_data_release(wrapper);
if (rwrapper)
obs_data_release(rwrapper);
wrapper = NULL;
} }
struct SelectedItemBounds { struct SelectedItemBounds {
@ -1434,6 +1477,8 @@ void OBSBasicPreview::StretchItem(const vec2 &pos)
void OBSBasicPreview::mouseMoveEvent(QMouseEvent *event) void OBSBasicPreview::mouseMoveEvent(QMouseEvent *event)
{ {
changed = true;
if (scrollMode && event->buttons() == Qt::LeftButton) { if (scrollMode && event->buttons() == Qt::LeftButton) {
scrollingOffset.x += event->x() - scrollingFrom.x; scrollingOffset.x += event->x() - scrollingFrom.x;
scrollingOffset.y += event->y() - scrollingFrom.y; scrollingOffset.y += event->y() - scrollingFrom.y;

View File

@ -106,6 +106,9 @@ private:
void ProcessClick(const vec2 &pos); void ProcessClick(const vec2 &pos);
obs_data_t *wrapper = NULL;
bool changed;
public: public:
OBSBasicPreview(QWidget *parent, OBSBasicPreview(QWidget *parent,
Qt::WindowFlags flags = Qt::WindowFlags()); Qt::WindowFlags flags = Qt::WindowFlags());

View File

@ -73,10 +73,42 @@ OBSBasicTransform::OBSBasicTransform(OBSBasic *parent)
SetScene(scene); SetScene(scene);
SetItem(item); SetItem(item);
obs_data_t *wrapper = obs_scene_save_transform_states(scene, false);
undo_data = std::string(obs_data_get_json(wrapper));
obs_data_release(wrapper);
channelChangedSignal.Connect(obs_get_signal_handler(), "channel_change", channelChangedSignal.Connect(obs_get_signal_handler(), "channel_change",
OBSChannelChanged, this); OBSChannelChanged, this);
} }
OBSBasicTransform::~OBSBasicTransform()
{
obs_data_t *wrapper =
obs_scene_save_transform_states(main->GetCurrentScene(), false);
auto undo_redo = [](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, "scene_name"));
reinterpret_cast<OBSBasic *>(App()->GetMainWindow())
->SetCurrentScene(source);
obs_source_release(source);
obs_data_release(dat);
obs_scene_load_transform_states(data.c_str());
};
std::string redo_data(obs_data_get_json(wrapper));
if (undo_data.compare(redo_data) != 0)
main->undo_s.add_action(
QTStr("Undo.Transform")
.arg(obs_source_get_name(obs_scene_get_source(
main->GetCurrentScene()))),
undo_redo, undo_redo, undo_data, redo_data, NULL);
obs_data_release(wrapper);
}
void OBSBasicTransform::SetScene(OBSScene scene) void OBSBasicTransform::SetScene(OBSScene scene)
{ {
transformSignal.Disconnect(); transformSignal.Disconnect();

View File

@ -21,6 +21,8 @@ private:
OBSSignal selectSignal; OBSSignal selectSignal;
OBSSignal deselectSignal; OBSSignal deselectSignal;
std::string undo_data;
bool ignoreTransformSignal = false; bool ignoreTransformSignal = false;
bool ignoreItemChange = false; bool ignoreItemChange = false;
@ -46,4 +48,5 @@ private slots:
public: public:
OBSBasicTransform(OBSBasic *parent); OBSBasicTransform(OBSBasic *parent);
~OBSBasicTransform();
}; };

View File

@ -1890,6 +1890,113 @@ static void signal_parent(obs_scene_t *parent, const char *command,
signal_handler_signal(parent->source->context.signals, command, params); signal_handler_signal(parent->source->context.signals, command, params);
} }
struct passthrough {
obs_data_array_t *ids;
bool all_items;
};
bool save_transform_states(obs_scene_t *scene, obs_sceneitem_t *item,
void *vp_pass)
{
struct passthrough *pass = (struct passthrough *)vp_pass;
if (obs_sceneitem_selected(item) || pass->all_items) {
obs_data_t *temp = obs_data_create();
obs_data_array_t *item_ids = (obs_data_array_t *)pass->ids;
struct obs_transform_info info;
struct obs_sceneitem_crop crop;
obs_sceneitem_get_info(item, &info);
obs_sceneitem_get_crop(item, &crop);
struct vec2 pos = info.pos;
struct vec2 scale = info.scale;
float rot = info.rot;
uint32_t alignment = info.alignment;
uint32_t bounds_type = info.bounds_type;
uint32_t bounds_alignment = info.bounds_alignment;
struct vec2 bounds = info.bounds;
obs_data_set_int(temp, "id", obs_sceneitem_get_id(item));
obs_data_set_vec2(temp, "pos", &pos);
obs_data_set_vec2(temp, "scale", &scale);
obs_data_set_int(temp, "rot", rot);
obs_data_set_int(temp, "alignment", alignment);
obs_data_set_int(temp, "bounds_type", bounds_type);
obs_data_set_vec2(temp, "bounds", &bounds);
obs_data_set_int(temp, "bounds_alignment", bounds_alignment);
obs_data_set_int(temp, "top", crop.top);
obs_data_set_int(temp, "bottom", crop.bottom);
obs_data_set_int(temp, "left", crop.left);
obs_data_set_int(temp, "right", crop.right);
obs_data_array_push_back(item_ids, temp);
obs_data_release(temp);
}
UNUSED_PARAMETER(scene);
return true;
}
obs_data_t *obs_scene_save_transform_states(obs_scene_t *scene, bool all_items)
{
obs_data_t *wrapper = obs_data_create();
obs_data_array_t *item_ids = obs_data_array_create();
struct passthrough pass = {item_ids, all_items};
obs_scene_enum_items(scene, save_transform_states, (void *)&pass);
obs_data_set_array(wrapper, "item_ids", item_ids);
obs_data_set_string(wrapper, "scene_name",
obs_source_get_name(obs_scene_get_source(scene)));
obs_data_array_release(item_ids);
return wrapper;
}
void load_transform_states(obs_data_t *temp, void *vp_scene)
{
obs_scene_t *scene = (obs_scene_t *)vp_scene;
int64_t id = obs_data_get_int(temp, "id");
obs_sceneitem_t *item = obs_scene_find_sceneitem_by_id(scene, id);
struct obs_transform_info info;
struct obs_sceneitem_crop crop;
obs_data_get_vec2(temp, "pos", &info.pos);
obs_data_get_vec2(temp, "scale", &info.scale);
info.rot = obs_data_get_int(temp, "rot");
info.alignment = obs_data_get_int(temp, "alignment");
info.bounds_type =
(enum obs_bounds_type)obs_data_get_int(temp, "bounds_type");
info.bounds_alignment = obs_data_get_int(temp, "bounds_alignment");
obs_data_get_vec2(temp, "bounds", &info.bounds);
crop.top = obs_data_get_int(temp, "top");
crop.bottom = obs_data_get_int(temp, "bottom");
crop.left = obs_data_get_int(temp, "left");
crop.right = obs_data_get_int(temp, "right");
obs_sceneitem_defer_update_begin(item);
obs_sceneitem_set_info(item, &info);
obs_sceneitem_set_crop(item, &crop);
obs_sceneitem_defer_update_end(item);
}
void obs_scene_load_transform_states(const char *data)
{
obs_data_t *dat = obs_data_create_from_json(data);
obs_data_array_t *item_ids = obs_data_get_array(dat, "item_ids");
obs_source_t *source =
obs_get_source_by_name(obs_data_get_string(dat, "scene_name"));
obs_scene_t *scene = obs_scene_from_source(source);
obs_data_array_enum(item_ids, load_transform_states, (void *)scene);
obs_data_release(dat);
obs_data_array_release(item_ids);
obs_source_release(source);
}
void obs_sceneitem_select(obs_sceneitem_t *item, bool select) void obs_sceneitem_select(obs_sceneitem_t *item, bool select)
{ {
struct calldata params; struct calldata params;

View File

@ -1598,6 +1598,14 @@ 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 */ /** 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, EXPORT obs_sceneitem_t *obs_scene_sceneitem_from_source(obs_scene_t *scene,
obs_source_t *source); obs_source_t *source);
/** Save all the transform states for a current scene's sceneitems */
EXPORT obs_data_t *obs_scene_save_transform_states(obs_scene_t *scene,
bool all_items);
/** Load all the transform states of sceneitems in that scene */
EXPORT void obs_scene_load_transform_states(const char *state);
/** Gets a sceneitem's order in its scene */ /** Gets a sceneitem's order in its scene */
EXPORT int obs_sceneitem_get_order_position(obs_sceneitem_t *item); EXPORT int obs_sceneitem_get_order_position(obs_sceneitem_t *item);