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.
This commit is contained in:
parent
eced5a320b
commit
60d95cb5bd
@ -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)
|
||||
|
@ -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,6 +2038,22 @@
|
||||
<string>Basic.MainMenu.View.ContextBar</string>
|
||||
</property>
|
||||
</action>
|
||||
<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>
|
||||
|
@ -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()));
|
||||
}
|
||||
|
@ -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)) {
|
||||
|
||||
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);
|
||||
}
|
||||
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,11 +5257,12 @@ 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QMenu *OBSBasic::CreateAddSourcePopupMenu()
|
||||
@ -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))
|
||||
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);
|
||||
} else {
|
||||
if (removeMultiple(items.size())) {
|
||||
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();
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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"))
|
||||
|
@ -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)
|
||||
{
|
||||
|
23
libobs/obs.h
23
libobs/obs.h
@ -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);
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user