|
@@ -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(§ions);
|
|
|
+
|
|
|
+ for (const String §ion : 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);
|