UI: Undo/Redo Properties and Filters
Implements undo/redo for both properties and filters. Works by creating a new callback that gets called to save undo/redo states after a timer is fired. Also disabled undo/redo until the actions have completed to prevent a user from being able to disrupt the stack by perfoming actions before others have finished.
This commit is contained in:
parent
999495ca8c
commit
86eb7aeb69
@ -20,6 +20,7 @@
|
||||
#include <QStackedWidget>
|
||||
#include <QDir>
|
||||
#include <QGroupBox>
|
||||
#include <QObject>
|
||||
#include "double-slider.hpp"
|
||||
#include "slider-ignorewheel.hpp"
|
||||
#include "spinbox-ignorewheel.hpp"
|
||||
@ -31,6 +32,9 @@
|
||||
|
||||
#include <cstdlib>
|
||||
#include <initializer_list>
|
||||
#include <obs-data.h>
|
||||
#include <obs.h>
|
||||
#include <qtimer.h>
|
||||
#include <string>
|
||||
|
||||
using namespace std;
|
||||
@ -171,13 +175,14 @@ void OBSPropertiesView::GetScrollPos(int &h, int &v)
|
||||
OBSPropertiesView::OBSPropertiesView(OBSData settings_, void *obj_,
|
||||
PropertiesReloadCallback reloadCallback,
|
||||
PropertiesUpdateCallback callback_,
|
||||
int minSize_)
|
||||
PropertiesVisualUpdateCb cb_, int minSize_)
|
||||
: VScrollArea(nullptr),
|
||||
properties(nullptr, obs_properties_destroy),
|
||||
settings(settings_),
|
||||
obj(obj_),
|
||||
reloadCallback(reloadCallback),
|
||||
callback(callback_),
|
||||
cb(cb_),
|
||||
minSize(minSize_)
|
||||
{
|
||||
setFrameShape(QFrame::NoFrame);
|
||||
@ -1885,6 +1890,12 @@ void WidgetInfo::ControlChanged()
|
||||
const char *setting = obs_property_name(property);
|
||||
obs_property_type type = obs_property_get_type(property);
|
||||
|
||||
if (!recently_updated) {
|
||||
old_settings_cache = obs_data_create();
|
||||
obs_data_apply(old_settings_cache, view->settings);
|
||||
obs_data_release(old_settings_cache);
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case OBS_PROPERTY_INVALID:
|
||||
return;
|
||||
@ -1933,8 +1944,32 @@ void WidgetInfo::ControlChanged()
|
||||
break;
|
||||
}
|
||||
|
||||
if (view->callback && !view->deferUpdate)
|
||||
view->callback(view->obj, view->settings);
|
||||
if (!recently_updated) {
|
||||
recently_updated = true;
|
||||
update_timer = new QTimer;
|
||||
connect(update_timer, &QTimer::timeout,
|
||||
[this, &ru = recently_updated]() {
|
||||
if (view->callback && !view->deferUpdate) {
|
||||
view->callback(view->obj,
|
||||
old_settings_cache,
|
||||
view->settings);
|
||||
}
|
||||
|
||||
ru = false;
|
||||
});
|
||||
connect(update_timer, &QTimer::timeout, &QTimer::deleteLater);
|
||||
update_timer->setSingleShot(true);
|
||||
}
|
||||
|
||||
if (update_timer) {
|
||||
update_timer->stop();
|
||||
update_timer->start(500);
|
||||
} else {
|
||||
blog(LOG_DEBUG, "No update timer or no callback!");
|
||||
}
|
||||
|
||||
if (view->cb && !view->deferUpdate)
|
||||
view->cb(view->obj, view->settings);
|
||||
|
||||
view->SignalChanged();
|
||||
|
||||
|
@ -1,7 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include "vertical-scroll-area.hpp"
|
||||
#include <obs-data.h>
|
||||
#include <obs.hpp>
|
||||
#include <qtimer.h>
|
||||
#include <QPointer>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
@ -10,7 +13,9 @@ class OBSPropertiesView;
|
||||
class QLabel;
|
||||
|
||||
typedef obs_properties_t *(*PropertiesReloadCallback)(void *obj);
|
||||
typedef void (*PropertiesUpdateCallback)(void *obj, obs_data_t *settings);
|
||||
typedef void (*PropertiesUpdateCallback)(void *obj, obs_data_t *old_settings,
|
||||
obs_data_t *new_settings);
|
||||
typedef void (*PropertiesVisualUpdateCb)(void *obj, obs_data_t *settings);
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
@ -23,6 +28,9 @@ private:
|
||||
OBSPropertiesView *view;
|
||||
obs_property_t *property;
|
||||
QWidget *widget;
|
||||
QPointer<QTimer> update_timer;
|
||||
bool recently_updated = false;
|
||||
OBSData old_settings_cache;
|
||||
|
||||
void BoolChanged(const char *setting);
|
||||
void IntChanged(const char *setting);
|
||||
@ -47,6 +55,15 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
~WidgetInfo()
|
||||
{
|
||||
if (update_timer) {
|
||||
update_timer->stop();
|
||||
update_timer->deleteLater();
|
||||
obs_data_release(old_settings_cache);
|
||||
}
|
||||
}
|
||||
|
||||
public slots:
|
||||
|
||||
void ControlChanged();
|
||||
@ -83,6 +100,7 @@ private:
|
||||
std::string type;
|
||||
PropertiesReloadCallback reloadCallback;
|
||||
PropertiesUpdateCallback callback = nullptr;
|
||||
PropertiesVisualUpdateCb cb = nullptr;
|
||||
int minSize;
|
||||
std::vector<std::unique_ptr<WidgetInfo>> children;
|
||||
std::string lastFocused;
|
||||
@ -135,13 +153,15 @@ signals:
|
||||
public:
|
||||
OBSPropertiesView(OBSData settings, void *obj,
|
||||
PropertiesReloadCallback reloadCallback,
|
||||
PropertiesUpdateCallback callback, int minSize = 0);
|
||||
PropertiesUpdateCallback callback,
|
||||
PropertiesVisualUpdateCb cb = nullptr,
|
||||
int minSize = 0);
|
||||
OBSPropertiesView(OBSData settings, const char *type,
|
||||
PropertiesReloadCallback reloadCallback,
|
||||
int minSize = 0);
|
||||
|
||||
inline obs_data_t *GetSettings() const { return settings; }
|
||||
|
||||
inline void UpdateSettings() { callback(obj, settings); }
|
||||
inline void UpdateSettings() { callback(obj, nullptr, settings); }
|
||||
inline bool DeferUpdate() const { return deferUpdate; }
|
||||
};
|
||||
|
@ -15,6 +15,7 @@
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
#include "properties-view.hpp"
|
||||
#include "window-namedialog.hpp"
|
||||
#include "window-basic-main.hpp"
|
||||
#include "window-basic-filters.hpp"
|
||||
@ -23,9 +24,13 @@
|
||||
#include "visibility-item-widget.hpp"
|
||||
#include "item-widget-helpers.hpp"
|
||||
#include "obs-app.hpp"
|
||||
#include "undo-stack-obs.hpp"
|
||||
|
||||
#include <QMessageBox>
|
||||
#include <QCloseEvent>
|
||||
#include <obs-data.h>
|
||||
#include <obs.h>
|
||||
#include <util/base.h>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <QMenu>
|
||||
@ -202,10 +207,82 @@ void OBSBasicFilters::UpdatePropertiesView(int row, bool async)
|
||||
|
||||
obs_data_t *settings = obs_source_get_settings(filter);
|
||||
|
||||
auto filter_change = [](void *vp, obs_data_t *nd_old_settings,
|
||||
obs_data_t *new_settings) {
|
||||
obs_source_t *source = reinterpret_cast<obs_source_t *>(vp);
|
||||
obs_source_t *parent = obs_filter_get_parent(source);
|
||||
const char *source_name = obs_source_get_name(source);
|
||||
OBSBasic *main = OBSBasic::Get();
|
||||
|
||||
obs_data_t *redo_wrapper = obs_data_create();
|
||||
obs_data_set_string(redo_wrapper, "name", source_name);
|
||||
obs_data_set_string(redo_wrapper, "settings",
|
||||
obs_data_get_json(new_settings));
|
||||
obs_data_set_string(redo_wrapper, "parent",
|
||||
obs_source_get_name(parent));
|
||||
|
||||
obs_data_t *filter_settings = obs_source_get_settings(source);
|
||||
obs_data_t *old_settings =
|
||||
obs_data_get_defaults(filter_settings);
|
||||
obs_data_apply(old_settings, nd_old_settings);
|
||||
|
||||
obs_data_t *undo_wrapper = obs_data_create();
|
||||
obs_data_set_string(undo_wrapper, "name", source_name);
|
||||
obs_data_set_string(undo_wrapper, "settings",
|
||||
obs_data_get_json(old_settings));
|
||||
obs_data_set_string(undo_wrapper, "parent",
|
||||
obs_source_get_name(parent));
|
||||
|
||||
auto undo_redo = [](const std::string &data) {
|
||||
obs_data_t *dat =
|
||||
obs_data_create_from_json(data.c_str());
|
||||
obs_source_t *parent_source = obs_get_source_by_name(
|
||||
obs_data_get_string(dat, "parent"));
|
||||
const char *filter_name =
|
||||
obs_data_get_string(dat, "name");
|
||||
obs_source_t *filter = obs_source_get_filter_by_name(
|
||||
parent_source, filter_name);
|
||||
obs_data_t *settings = obs_data_create_from_json(
|
||||
obs_data_get_string(dat, "settings"));
|
||||
|
||||
obs_source_update(filter, settings);
|
||||
obs_source_update_properties(filter);
|
||||
|
||||
obs_data_release(dat);
|
||||
obs_data_release(settings);
|
||||
obs_source_release(filter);
|
||||
obs_source_release(parent_source);
|
||||
};
|
||||
std::string name = std::string(obs_source_get_name(source));
|
||||
std::string undo_data = obs_data_get_json(undo_wrapper);
|
||||
std::string redo_data = obs_data_get_json(redo_wrapper);
|
||||
main->undo_s.add_action(QTStr("Undo.Filters").arg(name.c_str()),
|
||||
undo_redo, undo_redo, undo_data,
|
||||
redo_data, NULL);
|
||||
|
||||
obs_data_release(redo_wrapper);
|
||||
obs_data_release(undo_wrapper);
|
||||
obs_data_release(old_settings);
|
||||
obs_data_release(filter_settings);
|
||||
|
||||
obs_source_update(source, new_settings);
|
||||
|
||||
main->undo_s.enable_undo_redo();
|
||||
};
|
||||
|
||||
auto disabled_undo = [](void *vp, obs_data_t *settings) {
|
||||
OBSBasic *main =
|
||||
reinterpret_cast<OBSBasic *>(App()->GetMainWindow());
|
||||
main->undo_s.disable_undo_redo();
|
||||
obs_source_t *source = reinterpret_cast<obs_source_t *>(vp);
|
||||
obs_source_update(source, settings);
|
||||
};
|
||||
|
||||
view = new OBSPropertiesView(
|
||||
settings, filter,
|
||||
(PropertiesReloadCallback)obs_source_properties,
|
||||
(PropertiesUpdateCallback)obs_source_update);
|
||||
(PropertiesUpdateCallback)filter_change,
|
||||
(PropertiesVisualUpdateCb)disabled_undo);
|
||||
|
||||
updatePropertiesSignal.Connect(obs_source_get_signal_handler(filter),
|
||||
"update_properties",
|
||||
@ -240,6 +317,7 @@ void OBSBasicFilters::AddFilter(OBSSource filter, bool focus)
|
||||
list->addItem(item);
|
||||
if (focus)
|
||||
list->setCurrentItem(item);
|
||||
|
||||
SetupVisibilityItem(list, item, filter);
|
||||
}
|
||||
|
||||
@ -486,6 +564,68 @@ void OBSBasicFilters::AddNewFilter(const char *id)
|
||||
return;
|
||||
}
|
||||
|
||||
obs_data_t *wrapper = obs_data_create();
|
||||
obs_data_set_string(wrapper, "sname",
|
||||
obs_source_get_name(source));
|
||||
obs_data_set_string(wrapper, "fname", name.c_str());
|
||||
std::string scene_name = obs_source_get_name(
|
||||
reinterpret_cast<OBSBasic *>(App()->GetMainWindow())
|
||||
->GetCurrentSceneSource());
|
||||
auto undo = [scene_name](const std::string &data) {
|
||||
obs_source_t *ssource =
|
||||
obs_get_source_by_name(scene_name.c_str());
|
||||
reinterpret_cast<OBSBasic *>(App()->GetMainWindow())
|
||||
->SetCurrentScene(ssource);
|
||||
obs_source_release(ssource);
|
||||
|
||||
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, "sname"));
|
||||
obs_source_t *filter = obs_source_get_filter_by_name(
|
||||
source, obs_data_get_string(dat, "fname"));
|
||||
obs_source_filter_remove(source, filter);
|
||||
|
||||
obs_data_release(dat);
|
||||
obs_source_release(source);
|
||||
obs_source_release(filter);
|
||||
};
|
||||
|
||||
obs_data_t *rwrapper = obs_data_create();
|
||||
obs_data_set_string(rwrapper, "sname",
|
||||
obs_source_get_name(source));
|
||||
auto redo = [scene_name, id = std::string(id),
|
||||
name](const std::string &data) {
|
||||
obs_source_t *ssource =
|
||||
obs_get_source_by_name(scene_name.c_str());
|
||||
reinterpret_cast<OBSBasic *>(App()->GetMainWindow())
|
||||
->SetCurrentScene(ssource);
|
||||
obs_source_release(ssource);
|
||||
|
||||
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, "sname"));
|
||||
|
||||
obs_source_t *filter = obs_source_create(
|
||||
id.c_str(), name.c_str(), nullptr, nullptr);
|
||||
if (filter) {
|
||||
obs_source_filter_add(source, filter);
|
||||
obs_source_release(filter);
|
||||
}
|
||||
|
||||
obs_data_release(dat);
|
||||
obs_source_release(source);
|
||||
};
|
||||
|
||||
std::string undo_data(obs_data_get_json(wrapper));
|
||||
std::string redo_data(obs_data_get_json(rwrapper));
|
||||
main->undo_s.add_action(QTStr("Undo.Add").arg(name.c_str()),
|
||||
undo, redo, undo_data, redo_data, NULL);
|
||||
|
||||
obs_data_release(wrapper);
|
||||
obs_data_release(rwrapper);
|
||||
|
||||
obs_source_t *filter =
|
||||
obs_source_create(id, name.c_str(), nullptr, nullptr);
|
||||
if (filter) {
|
||||
@ -674,8 +814,75 @@ void OBSBasicFilters::on_removeEffectFilter_clicked()
|
||||
{
|
||||
OBSSource filter = GetFilter(ui->effectFilters->currentRow(), false);
|
||||
if (filter) {
|
||||
if (QueryRemove(this, filter))
|
||||
if (QueryRemove(this, filter)) {
|
||||
obs_data_t *wrapper = obs_save_source(filter);
|
||||
std::string parent_name(obs_source_get_name(source));
|
||||
obs_data_set_string(wrapper, "undo_name",
|
||||
parent_name.c_str());
|
||||
|
||||
std::string scene_name = obs_source_get_name(
|
||||
reinterpret_cast<OBSBasic *>(
|
||||
App()->GetMainWindow())
|
||||
->GetCurrentSceneSource());
|
||||
auto undo = [scene_name](const std::string &data) {
|
||||
obs_source_t *ssource = obs_get_source_by_name(
|
||||
scene_name.c_str());
|
||||
reinterpret_cast<OBSBasic *>(
|
||||
App()->GetMainWindow())
|
||||
->SetCurrentScene(ssource);
|
||||
obs_source_release(ssource);
|
||||
|
||||
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, "undo_name"));
|
||||
obs_source_t *filter = obs_load_source(dat);
|
||||
obs_source_filter_add(source, filter);
|
||||
|
||||
obs_data_release(dat);
|
||||
obs_source_release(source);
|
||||
obs_source_release(filter);
|
||||
};
|
||||
|
||||
obs_data_t *rwrapper = obs_data_create();
|
||||
obs_data_set_string(rwrapper, "fname",
|
||||
obs_source_get_name(filter));
|
||||
obs_data_set_string(rwrapper, "sname",
|
||||
parent_name.c_str());
|
||||
auto redo = [scene_name](const std::string &data) {
|
||||
obs_source_t *ssource = obs_get_source_by_name(
|
||||
scene_name.c_str());
|
||||
reinterpret_cast<OBSBasic *>(
|
||||
App()->GetMainWindow())
|
||||
->SetCurrentScene(ssource);
|
||||
obs_source_release(ssource);
|
||||
|
||||
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, "sname"));
|
||||
obs_source_t *filter =
|
||||
obs_source_get_filter_by_name(
|
||||
source, obs_data_get_string(
|
||||
dat, "fname"));
|
||||
obs_source_filter_remove(source, filter);
|
||||
|
||||
obs_data_release(dat);
|
||||
obs_source_release(filter);
|
||||
obs_source_release(source);
|
||||
};
|
||||
|
||||
std::string undo_data(obs_data_get_json(wrapper));
|
||||
std::string redo_data(obs_data_get_json(rwrapper));
|
||||
main->undo_s.add_action(
|
||||
QTStr("Undo.Delete")
|
||||
.arg(obs_source_get_name(filter)),
|
||||
undo, redo, undo_data, redo_data, NULL);
|
||||
obs_source_filter_remove(source, filter);
|
||||
|
||||
obs_data_release(wrapper);
|
||||
obs_data_release(rwrapper);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -918,6 +1125,48 @@ void OBSBasicFilters::FilterNameEdited(QWidget *editor, QListWidget *list)
|
||||
|
||||
listItem->setText(QT_UTF8(name.c_str()));
|
||||
obs_source_set_name(filter, name.c_str());
|
||||
|
||||
std::string scene_name = obs_source_get_name(
|
||||
reinterpret_cast<OBSBasic *>(App()->GetMainWindow())
|
||||
->GetCurrentSceneSource());
|
||||
auto undo = [scene_name, prev = std::string(prevName),
|
||||
name](const std::string &data) {
|
||||
obs_source_t *ssource =
|
||||
obs_get_source_by_name(scene_name.c_str());
|
||||
reinterpret_cast<OBSBasic *>(App()->GetMainWindow())
|
||||
->SetCurrentScene(ssource);
|
||||
obs_source_release(ssource);
|
||||
|
||||
obs_source_t *source =
|
||||
obs_get_source_by_name(data.c_str());
|
||||
obs_source_t *filter = obs_source_get_filter_by_name(
|
||||
source, name.c_str());
|
||||
obs_source_set_name(filter, prev.c_str());
|
||||
obs_source_release(source);
|
||||
obs_source_release(filter);
|
||||
};
|
||||
|
||||
auto redo = [scene_name, prev = std::string(prevName),
|
||||
name](const std::string &data) {
|
||||
obs_source_t *ssource =
|
||||
obs_get_source_by_name(scene_name.c_str());
|
||||
reinterpret_cast<OBSBasic *>(App()->GetMainWindow())
|
||||
->SetCurrentScene(ssource);
|
||||
obs_source_release(ssource);
|
||||
|
||||
obs_source_t *source =
|
||||
obs_get_source_by_name(data.c_str());
|
||||
obs_source_t *filter = obs_source_get_filter_by_name(
|
||||
source, prev.c_str());
|
||||
obs_source_set_name(filter, name.c_str());
|
||||
obs_source_release(source);
|
||||
obs_source_release(filter);
|
||||
};
|
||||
|
||||
std::string undo_data(sourceName);
|
||||
std::string redo_data(sourceName);
|
||||
main->undo_s.add_action(QTStr("Undo.Rename").arg(name.c_str()),
|
||||
undo, redo, undo_data, redo_data, NULL);
|
||||
}
|
||||
|
||||
listItem->setText(QString());
|
||||
|
@ -26,6 +26,10 @@
|
||||
#include <QScreen>
|
||||
#include <QWindow>
|
||||
#include <QMessageBox>
|
||||
#include <obs-data.h>
|
||||
#include <obs.h>
|
||||
#include <qpointer.h>
|
||||
#include <util/c99defs.h>
|
||||
|
||||
using namespace std;
|
||||
|
||||
@ -74,14 +78,28 @@ OBSBasicProperties::OBSBasicProperties(QWidget *parent, OBSSource source_)
|
||||
/* The OBSData constructor increments the reference once */
|
||||
obs_data_release(oldSettings);
|
||||
|
||||
OBSData settings = obs_source_get_settings(source);
|
||||
OBSData nd_settings = obs_source_get_settings(source);
|
||||
OBSData settings = obs_data_get_defaults(nd_settings);
|
||||
obs_data_apply(settings, nd_settings);
|
||||
obs_data_apply(oldSettings, settings);
|
||||
obs_data_release(settings);
|
||||
obs_data_release(nd_settings);
|
||||
|
||||
auto handle_memory = [](void *vp, obs_data_t *old_settings,
|
||||
obs_data_t *new_settings) {
|
||||
obs_source_t *source = reinterpret_cast<obs_source_t *>(vp);
|
||||
|
||||
obs_source_update(source, new_settings);
|
||||
|
||||
UNUSED_PARAMETER(old_settings);
|
||||
UNUSED_PARAMETER(vp);
|
||||
};
|
||||
|
||||
view = new OBSPropertiesView(
|
||||
settings, source,
|
||||
nd_settings, source,
|
||||
(PropertiesReloadCallback)obs_source_properties,
|
||||
(PropertiesUpdateCallback)obs_source_update);
|
||||
(PropertiesUpdateCallback)handle_memory,
|
||||
(PropertiesVisualUpdateCb)obs_source_update);
|
||||
view->setMinimumHeight(150);
|
||||
|
||||
preview->setMinimumSize(20, 150);
|
||||
@ -341,6 +359,51 @@ void OBSBasicProperties::on_buttonBox_clicked(QAbstractButton *button)
|
||||
QDialogButtonBox::ButtonRole val = buttonBox->buttonRole(button);
|
||||
|
||||
if (val == QDialogButtonBox::AcceptRole) {
|
||||
|
||||
std::string scene_name =
|
||||
obs_source_get_name(main->GetCurrentSceneSource());
|
||||
|
||||
auto undo_redo = [scene_name](const std::string &data) {
|
||||
obs_data_t *settings =
|
||||
obs_data_create_from_json(data.c_str());
|
||||
obs_source_t *source = obs_get_source_by_name(
|
||||
obs_data_get_string(settings, "undo_sname"));
|
||||
obs_source_update(source, settings);
|
||||
|
||||
obs_source_update_properties(source);
|
||||
|
||||
obs_source_t *scene_source =
|
||||
obs_get_source_by_name(scene_name.c_str());
|
||||
|
||||
OBSBasic::Get()->SetCurrentScene(source);
|
||||
|
||||
obs_source_release(scene_source);
|
||||
|
||||
obs_data_release(settings);
|
||||
obs_source_release(source);
|
||||
};
|
||||
|
||||
obs_data_t *new_settings = obs_data_create();
|
||||
obs_data_t *curr_settings = obs_source_get_settings(source);
|
||||
obs_data_apply(new_settings, curr_settings);
|
||||
obs_data_set_string(new_settings, "undo_sname",
|
||||
obs_source_get_name(source));
|
||||
obs_data_set_string(oldSettings, "undo_sname",
|
||||
obs_source_get_name(source));
|
||||
|
||||
std::string undo_data(obs_data_get_json(oldSettings));
|
||||
std::string redo_data(obs_data_get_json(new_settings));
|
||||
|
||||
if (undo_data.compare(redo_data) != 0)
|
||||
main->undo_s.add_action(
|
||||
QTStr("Undo.Properties")
|
||||
.arg(obs_source_get_name(source)),
|
||||
undo_redo, undo_redo, undo_data, redo_data,
|
||||
NULL);
|
||||
|
||||
obs_data_release(new_settings);
|
||||
obs_data_release(curr_settings);
|
||||
|
||||
acceptClicked = true;
|
||||
close();
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user