Bläddra i källkod

UI: Recreate nested scenes on scene delete undo

gxalpha 4 år sedan
förälder
incheckning
e6dc6c2d94
1 ändrade filer med 106 tillägg och 22 borttagningar
  1. 106 22
      UI/window-basic-main.cpp

+ 106 - 22
UI/window-basic-main.cpp

@@ -3777,6 +3777,18 @@ static bool save_undo_source_enum(obs_scene_t *scene, obs_sceneitem_t *item,
 	return true;
 }
 
+static inline void RemoveSceneAndReleaseNested(obs_source_t *source)
+{
+	obs_source_remove(source);
+	auto cb = [](void *unused, obs_source_t *source) {
+		UNUSED_PARAMETER(unused);
+		if (strcmp(obs_source_get_id(source), "scene") == 0)
+			obs_scene_prune_sources(obs_scene_from_source(source));
+		return true;
+	};
+	obs_enum_scenes(cb, NULL);
+}
+
 void OBSBasic::RemoveSelectedScene()
 {
 	OBSScene scene = GetCurrentScene();
@@ -3791,47 +3803,68 @@ void OBSBasic::RemoveSelectedScene()
 	/* ------------------------------ */
 	/* save all sources in scene      */
 
-	obs_data_array_t *array = obs_data_array_create();
+	obs_data_array_t *sources_in_deleted_scene = obs_data_array_create();
 
-	obs_scene_enum_items(scene, save_undo_source_enum, array);
+	obs_scene_enum_items(scene, save_undo_source_enum,
+			     sources_in_deleted_scene);
 
 	obs_data_t *scene_data = obs_save_source(source);
-	obs_data_array_push_back(array, scene_data);
+	obs_data_array_push_back(sources_in_deleted_scene, scene_data);
 	obs_data_release(scene_data);
 
 	/* ----------------------------------------------- */
 	/* save all scenes and groups the scene is used in */
 
-	for (int i = 0; i < ui->scenes->count(); i++) {
-		QListWidgetItem *widget_item = ui->scenes->item(i);
-		auto item_scene = GetOBSRef<OBSScene>(widget_item);
-		if (scene == item_scene)
-			continue;
-		auto *item = obs_scene_find_source_recursive(
-			item_scene, obs_source_get_name(source));
+	obs_data_array_t *scene_used_in_other_scenes = obs_data_array_create();
+
+	struct other_scenes_cb_data {
+		obs_source_t *oldScene;
+		obs_data_array_t *scene_used_in_other_scenes;
+	} other_scenes_cb_data;
+	other_scenes_cb_data.oldScene = source;
+	other_scenes_cb_data.scene_used_in_other_scenes =
+		scene_used_in_other_scenes;
+
+	auto other_scenes_cb = [](void *data_ptr, obs_source_t *scene) {
+		struct other_scenes_cb_data *data =
+			(struct other_scenes_cb_data *)data_ptr;
+		if (strcmp(obs_source_get_name(scene),
+			   obs_source_get_name(data->oldScene)) == 0)
+			return true;
+		obs_sceneitem_t *item = obs_scene_find_source(
+			obs_group_or_scene_from_source(scene),
+			obs_source_get_name(data->oldScene));
 		if (item) {
-			scene_data = obs_save_source(obs_scene_get_source(
-				obs_sceneitem_get_scene(item)));
-			obs_data_array_push_back(array, scene_data);
+			obs_data_t *scene_data =
+				obs_save_source(obs_scene_get_source(
+					obs_sceneitem_get_scene(item)));
+			obs_data_array_push_back(
+				data->scene_used_in_other_scenes, scene_data);
 			obs_data_release(scene_data);
 		}
-	}
+		return true;
+	};
+	obs_enum_scenes(other_scenes_cb, &other_scenes_cb_data);
 
 	/* --------------------------- */
 	/* undo/redo                   */
 
 	auto undo = [this](const std::string &json) {
 		obs_data_t *base = obs_data_create_from_json(json.c_str());
-		obs_data_array_t *array = obs_data_get_array(base, "array");
+		obs_data_array_t *sources_in_deleted_scene =
+			obs_data_get_array(base, "sources_in_deleted_scene");
+		obs_data_array_t *scene_used_in_other_scenes =
+			obs_data_get_array(base, "scene_used_in_other_scenes");
 		int savedIndex = (int)obs_data_get_int(base, "index");
 		std::vector<obs_source_t *> sources;
 
 		/* create missing sources */
-		size_t count = obs_data_array_count(array);
+		size_t count = obs_data_array_count(sources_in_deleted_scene);
 		sources.reserve(count);
 
 		for (size_t i = 0; i < count; i++) {
-			obs_data_t *data = obs_data_array_item(array, i);
+			obs_data_t *data = obs_data_array_item(
+				sources_in_deleted_scene, i);
 			const char *name = obs_data_get_string(data, "name");
 
 			obs_source_t *source = obs_get_source_by_name(name);
@@ -3849,6 +3882,52 @@ void OBSBasic::RemoveSelectedScene()
 		for (obs_source_t *source : sources)
 			obs_source_load2(source);
 
+		/* Add scene to scenes and groups it was nested in */
+		for (size_t i = 0;
+		     i < obs_data_array_count(scene_used_in_other_scenes);
+		     i++) {
+			obs_data_t *data = obs_data_array_item(
+				scene_used_in_other_scenes, i);
+			const char *name = obs_data_get_string(data, "name");
+			obs_source_t *source = obs_get_source_by_name(name);
+
+			obs_data_t *settings =
+				obs_data_get_obj(data, "settings");
+			obs_data_array_t *items =
+				obs_data_get_array(settings, "items");
+
+			/* Clear scene, but keep a reference to all sources in the scene to make sure they don't get destroyed */
+			std::vector<obs_source_t *> existing_sources;
+			auto cb = [](obs_scene_t *scene, obs_sceneitem_t *item,
+				     void *data) {
+				UNUSED_PARAMETER(scene);
+				std::vector<obs_source_t *> *existing =
+					(std::vector<obs_source_t *> *)data;
+				obs_source_t *source =
+					obs_sceneitem_get_source(item);
+				obs_source_addref(source);
+				obs_sceneitem_remove(item);
+				existing->push_back(source);
+				return true;
+			};
+			obs_scene_enum_items(
+				obs_group_or_scene_from_source(source), cb,
+				(void *)&existing_sources);
+
+			/* Re-add sources to the scene */
+			obs_sceneitems_add(
+				obs_group_or_scene_from_source(source), items);
+
+			/* Release source references */
+			for (obs_source_t *source : existing_sources)
+				obs_source_release(source);
+
+			obs_data_array_release(items);
+			obs_data_release(settings);
+			obs_source_release(source);
+			obs_data_release(data);
+		}
+
 		obs_source_t *scene_source = sources.back();
 		OBSScene scene = obs_scene_from_source(scene_source);
 		SetCurrentScene(scene, true);
@@ -3865,31 +3944,36 @@ void OBSBasic::RemoveSelectedScene()
 		for (obs_source_t *source : sources)
 			obs_source_release(source);
 
-		obs_data_array_release(array);
+		obs_data_array_release(sources_in_deleted_scene);
+		obs_data_array_release(scene_used_in_other_scenes);
 		obs_data_release(base);
 	};
 
 	auto redo = [](const std::string &name) {
 		obs_source_t *source = obs_get_source_by_name(name.c_str());
-		obs_source_remove(source);
+		RemoveSceneAndReleaseNested(source);
 		obs_source_release(source);
 	};
 
 	obs_data_t *data = obs_data_create();
-	obs_data_set_array(data, "array", array);
+	obs_data_set_array(data, "sources_in_deleted_scene",
+			   sources_in_deleted_scene);
+	obs_data_set_array(data, "scene_used_in_other_scenes",
+			   scene_used_in_other_scenes);
 	obs_data_set_int(data, "index", ui->scenes->currentRow());
 
 	const char *scene_name = obs_source_get_name(source);
 	undo_s.add_action(QTStr("Undo.Delete").arg(scene_name), undo, redo,
 			  obs_data_get_json(data), scene_name);
 
-	obs_data_array_release(array);
+	obs_data_array_release(sources_in_deleted_scene);
+	obs_data_array_release(scene_used_in_other_scenes);
 	obs_data_release(data);
 
 	/* --------------------------- */
 	/* remove                      */
 
-	obs_source_remove(source);
+	RemoveSceneAndReleaseNested(source);
 
 	if (api)
 		api->on_event(OBS_FRONTEND_EVENT_SCENE_LIST_CHANGED);