Bläddra i källkod

Add persistent folding to Animation Library Editor

Illauriel 1 år sedan
förälder
incheckning
5de38f7fd3

+ 202 - 1
editor/plugins/animation_library_editor.cpp

@@ -30,7 +30,12 @@
 
 #include "animation_library_editor.h"
 
+#include "core/string/print_string.h"
+#include "core/string/ustring.h"
+#include "core/templates/vector.h"
+#include "core/variant/variant.h"
 #include "editor/editor_node.h"
+#include "editor/editor_paths.h"
 #include "editor/editor_settings.h"
 #include "editor/editor_string_names.h"
 #include "editor/editor_undo_redo_manager.h"
@@ -518,6 +523,8 @@ void AnimationLibraryEditor::_item_renamed() {
 	if (restore_text) {
 		ti->set_text(0, old_text);
 	}
+
+	_save_mixer_lib_folding(ti);
 }
 
 void AnimationLibraryEditor::_button_pressed(TreeItem *p_item, int p_column, int p_id, MouseButton p_button) {
@@ -670,6 +677,8 @@ void AnimationLibraryEditor::update_tree() {
 
 	TreeItem *root = tree->create_item();
 	List<StringName> libs;
+	Vector<uint64_t> collapsed_lib_ids = _load_mixer_libs_folding();
+
 	mixer->get_animation_library_list(&libs);
 
 	for (const StringName &K : libs) {
@@ -759,12 +768,203 @@ void AnimationLibraryEditor::update_tree() {
 					anitem->set_text(1, anim_path.get_file());
 				}
 			}
+
 			anitem->add_button(1, get_editor_theme_icon("Save"), ANIM_BUTTON_FILE, animation_library_is_foreign, TTR("Save animation to resource on disk."));
 			anitem->add_button(1, get_editor_theme_icon("Remove"), ANIM_BUTTON_DELETE, animation_library_is_foreign, TTR("Remove animation from Library."));
+
+			for (const uint64_t &lib_id : collapsed_lib_ids) {
+				Object *lib_obj = ObjectDB::get_instance(ObjectID(lib_id));
+				AnimationLibrary *cur_lib = Object::cast_to<AnimationLibrary>(lib_obj);
+				StringName M = mixer->get_animation_library_name(cur_lib);
+
+				if (M == K) {
+					libitem->set_collapsed_recursive(true);
+				}
+			}
 		}
 	}
 }
 
+void AnimationLibraryEditor::_save_mixer_lib_folding(TreeItem *p_item) {
+	//Check if ti is a library or animation
+	if (p_item->get_parent()->get_parent() != nullptr) {
+		return;
+	}
+
+	Ref<ConfigFile> config;
+	config.instantiate();
+
+	String path = EditorPaths::get_singleton()->get_project_settings_dir().path_join("lib_folding.cfg");
+	Error err = config->load(path);
+	if (err != OK && err != ERR_FILE_NOT_FOUND) {
+		ERR_PRINT("Error loading lib_folding.cfg: " + itos(err));
+	}
+
+	// Get unique identifier for this scene+mixer combination
+	String md = (mixer->get_tree()->get_edited_scene_root()->get_scene_file_path() + mixer->get_path()).md5_text();
+
+	PackedStringArray collapsed_lib_names;
+	PackedStringArray collapsed_lib_ids;
+
+	if (config->has_section(md)) {
+		collapsed_lib_names = String(config->get_value(md, "folding")).split("\n");
+		collapsed_lib_ids = String(config->get_value(md, "id")).split("\n");
+	}
+
+	String lib_name = p_item->get_text(0);
+
+	// Get library reference and check validity
+	Ref<AnimationLibrary> al;
+	uint64_t lib_id = 0;
+
+	if (mixer->has_animation_library(lib_name)) {
+		al = mixer->get_animation_library(lib_name);
+		ERR_FAIL_COND(al.is_null());
+		lib_id = uint64_t(al->get_instance_id());
+	} else {
+		ERR_PRINT("Library not found: " + lib_name);
+	}
+
+	int at = collapsed_lib_names.find(lib_name);
+	if (p_item->is_collapsed()) {
+		if (at != -1) {
+			//Entry exists and needs updating
+			collapsed_lib_ids.set(at, String::num_int64(lib_id + INT64_MIN));
+		} else {
+			//Check if it's a rename
+			int id_at = collapsed_lib_ids.find(String::num_int64(lib_id + INT64_MIN));
+			if (id_at != -1) {
+				//It's actually a rename
+				collapsed_lib_names.set(id_at, lib_name);
+			} else {
+				//It's a new entry
+				collapsed_lib_names.append(lib_name);
+				collapsed_lib_ids.append(String::num_int64(lib_id + INT64_MIN));
+			}
+		}
+	} else {
+		if (at != -1) {
+			collapsed_lib_names.remove_at(at);
+			collapsed_lib_ids.remove_at(at);
+		}
+	}
+
+	//Runtime IDs
+	config->set_value(md, "root", uint64_t(mixer->get_tree()->get_edited_scene_root()->get_instance_id()));
+	config->set_value(md, "mixer", uint64_t(mixer->get_instance_id()));
+
+	//Plan B recovery mechanism
+	config->set_value(md, "mixer_signature", _get_mixer_signature());
+
+	//Save folding state as text and runtime ID
+	config->set_value(md, "folding", String("\n").join(collapsed_lib_names));
+	config->set_value(md, "id", String("\n").join(collapsed_lib_ids));
+
+	err = config->save(path);
+	if (err != OK) {
+		ERR_PRINT("Error saving lib_folding.cfg: " + itos(err));
+	}
+}
+
+Vector<uint64_t> AnimationLibraryEditor::_load_mixer_libs_folding() {
+	Ref<ConfigFile> config;
+	config.instantiate();
+
+	String path = EditorPaths::get_singleton()->get_project_settings_dir().path_join("lib_folding.cfg");
+	Error err = config->load(path);
+	if (err != OK && err != ERR_FILE_NOT_FOUND) {
+		ERR_PRINT("Error loading lib_folding.cfg: " + itos(err));
+		return Vector<uint64_t>();
+	}
+
+	// Get unique identifier for this scene+mixer combination
+	String md = (mixer->get_tree()->get_edited_scene_root()->get_scene_file_path() + mixer->get_path()).md5_text();
+
+	Vector<uint64_t> collapsed_lib_ids;
+
+	if (config->has_section(md)) {
+		_load_config_libs_folding(collapsed_lib_ids, config.ptr(), md);
+
+	} else {
+		//The scene/mixer combination is no longer valid and we'll try to recover
+		uint64_t current_mixer_id = uint64_t(mixer->get_instance_id());
+		String current_mixer_signature = _get_mixer_signature();
+		List<String> sections;
+		config->get_sections(&sections);
+
+		for (const String &section : sections) {
+			Variant mixer_id = config->get_value(section, "mixer");
+			if ((mixer_id.get_type() == Variant::INT && uint64_t(mixer_id) == current_mixer_id) || config->get_value(section, "mixer_signature") == current_mixer_signature) { // Ensure value exists and is correct type
+				// Found the mixer in a different section!
+				_load_config_libs_folding(collapsed_lib_ids, config.ptr(), section);
+
+				//Cleanup old entry and copy fold data into new one!
+				String collapsed_lib_names_str = String(config->get_value(section, "folding"));
+				String collapsed_lib_ids_str = String(config->get_value(section, "id"));
+				config->erase_section(section);
+
+				config->set_value(md, "root", uint64_t(mixer->get_tree()->get_edited_scene_root()->get_instance_id()));
+				config->set_value(md, "mixer", uint64_t(mixer->get_instance_id()));
+				config->set_value(md, "mixer_signature", _get_mixer_signature());
+				config->set_value(md, "folding", collapsed_lib_names_str);
+				config->set_value(md, "id", collapsed_lib_ids_str);
+
+				err = config->save(path);
+				if (err != OK) {
+					ERR_PRINT("Error saving lib_folding.cfg: " + itos(err));
+				}
+				break;
+			}
+		}
+	}
+
+	return collapsed_lib_ids;
+}
+
+void AnimationLibraryEditor::_load_config_libs_folding(Vector<uint64_t> &p_lib_ids, ConfigFile *p_config, String p_section) {
+	if (uint64_t(p_config->get_value(p_section, "root", 0)) != uint64_t(mixer->get_tree()->get_edited_scene_root()->get_instance_id())) {
+		// Root changed - tries to match by library names
+		PackedStringArray collapsed_lib_names = String(p_config->get_value(p_section, "folding", "")).split("\n");
+		for (const String &lib_name : collapsed_lib_names) {
+			if (mixer->has_animation_library(lib_name)) {
+				p_lib_ids.append(mixer->get_animation_library(lib_name)->get_instance_id());
+			} else {
+				print_line("Can't find ", lib_name, " in mixer");
+			}
+		}
+	} else {
+		// Root same - uses saved instance IDs
+		for (const String &saved_id : String(p_config->get_value(p_section, "id")).split("\n")) {
+			p_lib_ids.append(uint64_t(saved_id.to_int() - INT64_MIN));
+		}
+	}
+}
+
+String AnimationLibraryEditor::_get_mixer_signature() const {
+	String signature = String();
+
+	// Get all libraries sorted for consistency
+	List<StringName> libs;
+	mixer->get_animation_library_list(&libs);
+	libs.sort_custom<StringName::AlphCompare>();
+
+	// Add libraries and their animations to signature
+	for (const StringName &lib_name : libs) {
+		signature += "::" + String(lib_name);
+		Ref<AnimationLibrary> lib = mixer->get_animation_library(lib_name);
+		if (lib.is_valid()) {
+			List<StringName> anims;
+			lib->get_animation_list(&anims);
+			anims.sort_custom<StringName::AlphCompare>();
+			for (const StringName &anim_name : anims) {
+				signature += "," + String(anim_name);
+			}
+		}
+	}
+
+	return signature.md5_text();
+}
+
 void AnimationLibraryEditor::show_dialog() {
 	update_tree();
 	popup_centered_ratio(0.5);
@@ -855,11 +1055,12 @@ AnimationLibraryEditor::AnimationLibraryEditor() {
 	tree->set_column_custom_minimum_width(1, EDSCALE * 250);
 	tree->set_column_expand(1, false);
 	tree->set_hide_root(true);
-	tree->set_hide_folding(true);
+	tree->set_hide_folding(false);
 	tree->set_v_size_flags(Control::SIZE_EXPAND_FILL);
 
 	tree->connect("item_edited", callable_mp(this, &AnimationLibraryEditor::_item_renamed));
 	tree->connect("button_clicked", callable_mp(this, &AnimationLibraryEditor::_button_pressed));
+	tree->connect("item_collapsed", callable_mp(this, &AnimationLibraryEditor::_save_mixer_lib_folding));
 
 	file_popup = memnew(PopupMenu);
 	add_child(file_popup);

+ 7 - 0
editor/plugins/animation_library_editor.h

@@ -31,6 +31,8 @@
 #ifndef ANIMATION_LIBRARY_EDITOR_H
 #define ANIMATION_LIBRARY_EDITOR_H
 
+#include "core/io/config_file.h"
+#include "core/templates/vector.h"
 #include "editor/animation_track_editor.h"
 #include "editor/plugins/editor_plugin.h"
 #include "scene/animation/animation_mixer.h"
@@ -103,6 +105,11 @@ class AnimationLibraryEditor : public AcceptDialog {
 	void _load_file(const String &p_path);
 	void _load_files(const PackedStringArray &p_paths);
 
+	void _save_mixer_lib_folding(TreeItem *p_item);
+	Vector<uint64_t> _load_mixer_libs_folding();
+	void _load_config_libs_folding(Vector<uint64_t> &p_lib_ids, ConfigFile *p_config, String p_section);
+	String _get_mixer_signature() const;
+
 	void _item_renamed();
 	void _button_pressed(TreeItem *p_item, int p_column, int p_id, MouseButton p_button);
 

+ 12 - 0
scene/animation/animation_mixer.cpp

@@ -33,6 +33,8 @@
 
 #include "core/config/engine.h"
 #include "core/config/project_settings.h"
+#include "core/string/print_string.h"
+#include "core/string/string_name.h"
 #include "scene/2d/audio_stream_player_2d.h"
 #include "scene/animation/animation_player.h"
 #include "scene/audio/audio_stream_player.h"
@@ -267,6 +269,16 @@ bool AnimationMixer::has_animation_library(const StringName &p_name) const {
 	return false;
 }
 
+StringName AnimationMixer::get_animation_library_name(const Ref<AnimationLibrary> &p_animation_library) const {
+	ERR_FAIL_COND_V(p_animation_library.is_null(), StringName());
+	for (const AnimationLibraryData &lib : animation_libraries) {
+		if (lib.library == p_animation_library) {
+			return lib.name;
+		}
+	}
+	return StringName();
+}
+
 StringName AnimationMixer::find_animation_library(const Ref<Animation> &p_animation) const {
 	for (const KeyValue<StringName, AnimationData> &E : animation_set) {
 		if (E.value.animation == p_animation) {

+ 1 - 0
scene/animation/animation_mixer.h

@@ -409,6 +409,7 @@ public:
 	void get_animation_library_list(List<StringName> *p_animations) const;
 	Ref<AnimationLibrary> get_animation_library(const StringName &p_name) const;
 	bool has_animation_library(const StringName &p_name) const;
+	StringName get_animation_library_name(const Ref<AnimationLibrary> &p_animation_library) const;
 	StringName find_animation_library(const Ref<Animation> &p_animation) const;
 	Error add_animation_library(const StringName &p_name, const Ref<AnimationLibrary> &p_animation_library);
 	void remove_animation_library(const StringName &p_name);