UI: Undo/Redo Transformations
Implements undo/redo for transformations of sources, both through preview and the transformations properties.
This commit is contained in:
parent
60d95cb5bd
commit
3a620c485e
@ -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, ¢erType);
|
obs_scene_enum_items(GetCurrentScene(), center_to_scene, ¢erType);
|
||||||
|
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, ¢erType);
|
obs_scene_enum_items(GetCurrentScene(), center_to_scene, ¢erType);
|
||||||
|
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, ¢erType);
|
obs_scene_enum_items(GetCurrentScene(), center_to_scene, ¢erType);
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
|
@ -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());
|
||||||
|
@ -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();
|
||||||
|
@ -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();
|
||||||
};
|
};
|
||||||
|
@ -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;
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user