diff options
Diffstat (limited to 'editor')
83 files changed, 4085 insertions, 2693 deletions
diff --git a/editor/code_editor.cpp b/editor/code_editor.cpp index 29275947a4..9d4333bc29 100644 --- a/editor/code_editor.cpp +++ b/editor/code_editor.cpp @@ -745,7 +745,7 @@ bool CodeTextEditor::_add_font_size(int p_delta) { if (font.is_valid()) { int new_size = CLAMP(font->get_size() + p_delta, 8 * EDSCALE, 96 * EDSCALE); - zoom_nb->set_text(itos(100 * new_size / 14) + "%"); + zoom_nb->set_text(itos(100 * new_size / (14 * EDSCALE)) + "%"); if (new_size != font->get_size()) { EditorSettings::get_singleton()->set("interface/editor/code_font_size", new_size / EDSCALE); @@ -1341,7 +1341,7 @@ CodeTextEditor::CodeTextEditor() { font_resize_val = 0; font_size = EditorSettings::get_singleton()->get("interface/editor/code_font_size"); - zoom_nb->set_text(itos(100 * font_size / 14) + "%"); + zoom_nb->set_text(itos(100 * font_size / (14 * EDSCALE)) + "%"); font_resize_timer = memnew(Timer); add_child(font_resize_timer); font_resize_timer->set_one_shot(true); diff --git a/editor/create_dialog.cpp b/editor/create_dialog.cpp index 3e0c1f2d53..8433f4ff7b 100644 --- a/editor/create_dialog.cpp +++ b/editor/create_dialog.cpp @@ -86,7 +86,7 @@ void CreateDialog::popup_create(bool p_dont_clear, bool p_replace_mode) { memdelete(f); } - _update_favorite_list(); + _save_and_update_favorite_list(); // Restore valid window bounds or pop up at default size. if (EditorSettings::get_singleton()->has_setting("interface/dialogs/create_new_node_bounds")) { @@ -157,6 +157,18 @@ Ref<Texture> CreateDialog::_get_editor_icon(const String &p_type) const { return get_icon(p_type, "EditorIcons"); } + if (ScriptServer::is_global_class(p_type)) { + String icon_path = EditorNode::get_editor_data().script_class_get_icon_path(p_type); + RES icon; + if (FileAccess::exists(icon_path)) { + icon = ResourceLoader::load(icon_path); + } + if (!icon.is_valid()) { + icon = get_icon(ScriptServer::get_global_class_base(p_type), "EditorIcons"); + } + return icon; + } + const Map<String, Vector<EditorData::CustomType> > &p_map = EditorNode::get_editor_data().get_custom_types(); for (const Map<String, Vector<EditorData::CustomType> >::Element *E = p_map.front(); E; E = E->next()) { const Vector<EditorData::CustomType> &ct = E->value(); @@ -180,7 +192,7 @@ void CreateDialog::add_type(const String &p_type, HashMap<String, TreeItem *> &p return; bool cpp_type = ClassDB::class_exists(p_type); - EditorData &ed = EditorNode::get_singleton()->get_editor_data(); + EditorData &ed = EditorNode::get_editor_data(); if (p_type == base_type) return; @@ -262,13 +274,7 @@ void CreateDialog::add_type(const String &p_type, HashMap<String, TreeItem *> &p const String &description = EditorHelp::get_doc_data()->class_list[p_type].brief_description; item->set_tooltip(0, description); - if (cpp_type && has_icon(p_type, "EditorIcons")) { - - item->set_icon(0, get_icon(p_type, "EditorIcons")); - } else if (!cpp_type && has_icon(ScriptServer::get_global_class_base(p_type), "EditorIcons")) { - - item->set_icon(0, get_icon(ScriptServer::get_global_class_base(p_type), "EditorIcons")); - } + item->set_icon(0, _get_editor_icon(p_type)); p_types[p_type] = item; } @@ -287,7 +293,7 @@ void CreateDialog::_update_search() { HashMap<String, TreeItem *> types; TreeItem *root = search_options->create_item(); - EditorData &ed = EditorNode::get_singleton()->get_editor_data(); + EditorData &ed = EditorNode::get_editor_data(); root->set_text(0, base_type); if (has_icon(base_type, "EditorIcons")) { @@ -330,7 +336,7 @@ void CreateDialog::_update_search() { break; } - type = ClassDB::get_parent_class(type); + type = cpp_type ? ClassDB::get_parent_class(type) : ed.script_class_get_base(type); } if (found) @@ -425,6 +431,8 @@ void CreateDialog::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: { connect("confirmed", this, "_confirmed"); + search_box->set_right_icon(get_icon("Search", "EditorIcons")); + search_box->set_clear_button_enabled(true); favorite->set_icon(get_icon("Favorites", "EditorIcons")); } break; case NOTIFICATION_EXIT_TREE: { @@ -488,16 +496,8 @@ Object *CreateDialog::instance_selected() { custom = md; if (custom != String()) { - if (ScriptServer::is_global_class(custom)) { - RES script = ResourceLoader::load(ScriptServer::get_global_class_path(custom)); - ERR_FAIL_COND_V(!script.is_valid(), NULL); - - Object *obj = ClassDB::instance(ScriptServer::get_global_class_base(custom)); - ERR_FAIL_COND_V(!obj, NULL); - - obj->set_script(script.get_ref_ptr()); - return obj; + return EditorNode::get_editor_data().script_class_instance(custom); } return EditorNode::get_editor_data().instance_custom_type(selected->get_text(0), custom); } else { @@ -543,8 +543,7 @@ void CreateDialog::_favorite_toggled() { favorite->set_pressed(false); } - _save_favorite_list(); - _update_favorite_list(); + _save_and_update_favorite_list(); } void CreateDialog::_save_favorite_list() { @@ -554,8 +553,11 @@ void CreateDialog::_save_favorite_list() { if (f) { for (int i = 0; i < favorite_list.size(); i++) { - - f->store_line(favorite_list[i]); + String l = favorite_list[i]; + String name = l.split(" ")[0]; + if (!(ClassDB::class_exists(name) || ScriptServer::is_global_class(name))) + continue; + f->store_line(l); } memdelete(f); } @@ -566,11 +568,15 @@ void CreateDialog::_update_favorite_list() { favorites->clear(); TreeItem *root = favorites->create_item(); for (int i = 0; i < favorite_list.size(); i++) { - TreeItem *ti = favorites->create_item(root); String l = favorite_list[i]; + String name = l.split(" ")[0]; + if (!(ClassDB::class_exists(name) || ScriptServer::is_global_class(name))) + continue; + TreeItem *ti = favorites->create_item(root); ti->set_text(0, l); ti->set_icon(0, _get_editor_icon(l)); } + emit_signal("favorites_updated"); } void CreateDialog::_history_selected() { @@ -579,7 +585,7 @@ void CreateDialog::_history_selected() { if (!item) return; - search_box->set_text(item->get_text(0)); + search_box->set_text(item->get_text(0).get_slicec(' ', 0)); _update_search(); } @@ -589,7 +595,7 @@ void CreateDialog::_favorite_selected() { if (!item) return; - search_box->set_text(item->get_text(0)); + search_box->set_text(item->get_text(0).get_slicec(' ', 0)); _update_search(); } @@ -673,6 +679,10 @@ void CreateDialog::drop_data_fw(const Point2 &p_point, const Variant &p_data, Co } } + _save_and_update_favorite_list(); +} + +void CreateDialog::_save_and_update_favorite_list() { _save_favorite_list(); _update_favorite_list(); } @@ -688,12 +698,14 @@ void CreateDialog::_bind_methods() { ClassDB::bind_method(D_METHOD("_favorite_selected"), &CreateDialog::_favorite_selected); ClassDB::bind_method(D_METHOD("_history_activated"), &CreateDialog::_history_activated); ClassDB::bind_method(D_METHOD("_favorite_activated"), &CreateDialog::_favorite_activated); + ClassDB::bind_method(D_METHOD("_save_and_update_favorite_list"), &CreateDialog::_save_and_update_favorite_list); ClassDB::bind_method("get_drag_data_fw", &CreateDialog::get_drag_data_fw); ClassDB::bind_method("can_drop_data_fw", &CreateDialog::can_drop_data_fw); ClassDB::bind_method("drop_data_fw", &CreateDialog::drop_data_fw); ADD_SIGNAL(MethodInfo("create")); + ADD_SIGNAL(MethodInfo("favorites_updated")); } CreateDialog::CreateDialog() { diff --git a/editor/create_dialog.h b/editor/create_dialog.h index f8eec231a4..6df9eebc8c 100644 --- a/editor/create_dialog.h +++ b/editor/create_dialog.h @@ -90,6 +90,8 @@ protected: void _notification(int p_what); static void _bind_methods(); + void _save_and_update_favorite_list(); + public: Object *instance_selected(); String get_selected_type(); diff --git a/editor/editor_data.cpp b/editor/editor_data.cpp index f4ef11eb36..69c120bb3c 100644 --- a/editor/editor_data.cpp +++ b/editor/editor_data.cpp @@ -888,11 +888,56 @@ StringName EditorData::script_class_get_base(const String &p_class) { return script->get_language()->get_global_class_name(base_script->get_path()); } +Object *EditorData::script_class_instance(const String &p_class) { + if (ScriptServer::is_global_class(p_class)) { + Object *obj = ClassDB::instance(ScriptServer::get_global_class_base(p_class)); + if (obj) { + RES script = ResourceLoader::load(ScriptServer::get_global_class_path(p_class)); + if (script.is_valid()) + obj->set_script(script.get_ref_ptr()); + + RES icon = ResourceLoader::load(script_class_get_icon_path(p_class)); + if (icon.is_valid()) + obj->set_meta("_editor_icon", icon); + + return obj; + } + } + return NULL; +} + +void EditorData::script_class_save_icon_paths() { + List<StringName> keys; + _script_class_icon_paths.get_key_list(&keys); + + Dictionary d; + for (List<StringName>::Element *E = keys.front(); E; E = E->next()) { + d[E->get()] = _script_class_icon_paths[E->get()]; + } + + ProjectSettings::get_singleton()->set("_global_script_class_icons", d); + ProjectSettings::get_singleton()->save(); +} + +void EditorData::script_class_load_icon_paths() { + script_class_clear_icon_paths(); + + Dictionary d = ProjectSettings::get_singleton()->get("_global_script_class_icons"); + List<Variant> keys; + d.get_key_list(&keys); + + for (List<Variant>::Element *E = keys.front(); E; E = E->next()) { + String key = E->get().operator String(); + _script_class_icon_paths[key] = d[key]; + } +} + EditorData::EditorData() { current_edited_scene = -1; //load_imported_scenes_from_globals(); + script_class_load_icon_paths(); } /////////// diff --git a/editor/editor_data.h b/editor/editor_data.h index fac6635cd2..285769aa78 100644 --- a/editor/editor_data.h +++ b/editor/editor_data.h @@ -146,6 +146,8 @@ private: bool _find_updated_instances(Node *p_root, Node *p_node, Set<String> &checked_paths); + HashMap<StringName, String> _script_class_icon_paths; + public: EditorPlugin *get_editor(Object *p_object); EditorPlugin *get_subeditor(Object *p_object); @@ -213,6 +215,12 @@ public: bool script_class_is_parent(const String &p_class, const String &p_inherits); StringName script_class_get_base(const String &p_class); + Object *script_class_instance(const String &p_class); + String script_class_get_icon_path(const String &p_class) const { return _script_class_icon_paths.has(p_class) ? _script_class_icon_paths[p_class] : String(); } + void script_class_set_icon_path(const String &p_class, const String &p_icon_path) { _script_class_icon_paths[p_class] = p_icon_path; } + void script_class_clear_icon_paths() { _script_class_icon_paths.clear(); } + void script_class_save_icon_paths(); + void script_class_load_icon_paths(); EditorData(); }; diff --git a/editor/editor_file_dialog.cpp b/editor/editor_file_dialog.cpp index 7d56c4985a..d240f4ed25 100644 --- a/editor/editor_file_dialog.cpp +++ b/editor/editor_file_dialog.cpp @@ -558,7 +558,9 @@ void EditorFileDialog::_item_list_item_rmb_selected(int p_item, const Vector2 &p } if (single_item_selected) { item_menu->add_separator(); - item_menu->add_icon_item(get_icon("Filesystem", "EditorIcons"), TTR("Show In File Manager"), ITEM_MENU_SHOW_IN_EXPLORER); + Dictionary item_meta = item_list->get_item_metadata(p_item); + String item_text = item_meta["dir"] ? TTR("Open In File Manager") : TTR("Show In File Manager"); + item_menu->add_icon_item(get_icon("Filesystem", "EditorIcons"), item_text, ITEM_MENU_SHOW_IN_EXPLORER); } if (item_menu->get_item_count() > 0) { @@ -582,7 +584,7 @@ void EditorFileDialog::_item_list_rmb_clicked(const Vector2 &p_pos) { } item_menu->add_icon_item(get_icon("Reload", "EditorIcons"), TTR("Refresh"), ITEM_MENU_REFRESH, KEY_F5); item_menu->add_separator(); - item_menu->add_icon_item(get_icon("Filesystem", "EditorIcons"), TTR("Show In File Manager"), ITEM_MENU_SHOW_IN_EXPLORER); + item_menu->add_icon_item(get_icon("Filesystem", "EditorIcons"), TTR("Open In File Manager"), ITEM_MENU_SHOW_IN_EXPLORER); item_menu->set_position(item_list->get_global_position() + p_pos); item_menu->popup(); diff --git a/editor/editor_file_system.cpp b/editor/editor_file_system.cpp index d8ab41fa05..9562a8c63c 100644 --- a/editor/editor_file_system.cpp +++ b/editor/editor_file_system.cpp @@ -133,6 +133,10 @@ String EditorFileSystemDirectory::get_file_script_class_extends(int p_idx) const return files[p_idx]->script_class_extends; } +String EditorFileSystemDirectory::get_file_script_class_icon_path(int p_idx) const { + return files[p_idx]->script_class_icon_path; +} + StringName EditorFileSystemDirectory::get_file_type(int p_idx) const { ERR_FAIL_INDEX_V(p_idx, files.size(), ""); @@ -233,6 +237,7 @@ void EditorFileSystem::_scan_filesystem() { fc.import_valid = split[4].to_int64() != 0; fc.script_class_name = split[5].get_slice("<>", 0); fc.script_class_extends = split[5].get_slice("<>", 1); + fc.script_class_icon_path = split[5].get_slice("<>", 2); String deps = split[6].strip_edges(); if (deps.length()) { @@ -721,6 +726,7 @@ void EditorFileSystem::_scan_new_dir(EditorFileSystemDirectory *p_dir, DirAccess fi->import_valid = fc->import_valid; fi->script_class_name = fc->script_class_name; fi->script_class_extends = fc->script_class_extends; + fi->script_class_icon_path = fc->script_class_icon_path; if (fc->type == String()) { fi->type = ResourceLoader::get_resource_type(path); @@ -731,7 +737,7 @@ void EditorFileSystem::_scan_new_dir(EditorFileSystemDirectory *p_dir, DirAccess } else { fi->type = ResourceFormatImporter::get_singleton()->get_resource_type(path); - fi->script_class_name = _get_global_script_class(fi->type, path, &fi->script_class_extends); + fi->script_class_name = _get_global_script_class(fi->type, path, &fi->script_class_extends, &fi->script_class_icon_path); fi->modified_time = 0; fi->import_modified_time = 0; fi->import_valid = ResourceLoader::is_import_valid(path); @@ -753,10 +759,11 @@ void EditorFileSystem::_scan_new_dir(EditorFileSystemDirectory *p_dir, DirAccess fi->import_valid = true; fi->script_class_name = fc->script_class_name; fi->script_class_extends = fc->script_class_extends; + fi->script_class_icon_path = fc->script_class_icon_path; } else { //new or modified time fi->type = ResourceLoader::get_resource_type(path); - fi->script_class_name = _get_global_script_class(fi->type, path, &fi->script_class_extends); + fi->script_class_name = _get_global_script_class(fi->type, path, &fi->script_class_extends, &fi->script_class_icon_path); fi->deps = _get_dependencies(path); fi->modified_time = mt; fi->import_modified_time = 0; @@ -855,7 +862,7 @@ void EditorFileSystem::_scan_fs_changes(EditorFileSystemDirectory *p_dir, const fi->modified_time = FileAccess::get_modified_time(path); fi->import_modified_time = 0; fi->type = ResourceLoader::get_resource_type(path); - fi->script_class_name = _get_global_script_class(fi->type, path, &fi->script_class_extends); + fi->script_class_name = _get_global_script_class(fi->type, path, &fi->script_class_extends, &fi->script_class_icon_path); fi->import_valid = ResourceLoader::is_import_valid(path); { @@ -1110,7 +1117,7 @@ void EditorFileSystem::_save_filesystem_cache(EditorFileSystemDirectory *p_dir, for (int i = 0; i < p_dir->files.size(); i++) { - String s = p_dir->files[i]->file + "::" + p_dir->files[i]->type + "::" + itos(p_dir->files[i]->modified_time) + "::" + itos(p_dir->files[i]->import_modified_time) + "::" + itos(p_dir->files[i]->import_valid) + "::" + p_dir->files[i]->script_class_name + "<>" + p_dir->files[i]->script_class_extends; + String s = p_dir->files[i]->file + "::" + p_dir->files[i]->type + "::" + itos(p_dir->files[i]->modified_time) + "::" + itos(p_dir->files[i]->import_modified_time) + "::" + itos(p_dir->files[i]->import_valid) + "::" + p_dir->files[i]->script_class_name + "<>" + p_dir->files[i]->script_class_extends + "<>" + p_dir->files[i]->script_class_icon_path; s += "::"; for (int j = 0; j < p_dir->files[i]->deps.size(); j++) { @@ -1316,19 +1323,22 @@ Vector<String> EditorFileSystem::_get_dependencies(const String &p_path) { return ret; } -String EditorFileSystem::_get_global_script_class(const String &p_type, const String &p_path, String *r_extends) const { +String EditorFileSystem::_get_global_script_class(const String &p_type, const String &p_path, String *r_extends, String *r_icon_path) const { for (int i = 0; i < ScriptServer::get_language_count(); i++) { if (ScriptServer::get_language(i)->handles_global_class_type(p_type)) { String global_name; String extends; + String icon_path; - global_name = ScriptServer::get_language(i)->get_global_class_name(p_path, &extends); + global_name = ScriptServer::get_language(i)->get_global_class_name(p_path, &extends, &icon_path); *r_extends = extends; + *r_icon_path = icon_path; return global_name; } } *r_extends = String(); + *r_icon_path = String(); return String(); } @@ -1346,8 +1356,8 @@ void EditorFileSystem::_scan_script_classes(EditorFileSystemDirectory *p_dir) { lang = ScriptServer::get_language(j)->get_name(); } } - ScriptServer::add_global_class(files[i]->script_class_name, files[i]->script_class_extends, lang, p_dir->get_file_path(i)); + EditorNode::get_editor_data().script_class_set_icon_path(files[i]->script_class_name, files[i]->script_class_icon_path); } for (int i = 0; i < p_dir->get_subdir_count(); i++) { _scan_script_classes(p_dir->get_subdir(i)); @@ -1366,6 +1376,8 @@ void EditorFileSystem::update_script_classes() { } ScriptServer::save_global_classes(); + EditorNode::get_editor_data().script_class_save_icon_paths(); + emit_signal("script_classes_updated"); } void EditorFileSystem::_queue_update_script_classes() { @@ -1437,7 +1449,7 @@ void EditorFileSystem::update_file(const String &p_file) { } fs->files[cpos]->type = type; - fs->files[cpos]->script_class_name = _get_global_script_class(type, p_file, &fs->files[cpos]->script_class_extends); + fs->files[cpos]->script_class_name = _get_global_script_class(type, p_file, &fs->files[cpos]->script_class_extends, &fs->files[cpos]->script_class_icon_path); fs->files[cpos]->modified_time = FileAccess::get_modified_time(p_file); fs->files[cpos]->deps = _get_dependencies(p_file); fs->files[cpos]->import_valid = ResourceLoader::is_import_valid(p_file); @@ -1704,6 +1716,7 @@ void EditorFileSystem::_bind_methods() { ADD_SIGNAL(MethodInfo("filesystem_changed")); ADD_SIGNAL(MethodInfo("sources_changed", PropertyInfo(Variant::BOOL, "exist"))); ADD_SIGNAL(MethodInfo("resources_reimported", PropertyInfo(Variant::POOL_STRING_ARRAY, "resources"))); + ADD_SIGNAL(MethodInfo("script_classes_updated")); } void EditorFileSystem::_update_extensions() { diff --git a/editor/editor_file_system.h b/editor/editor_file_system.h index 1aa35f4782..75ca79932f 100644 --- a/editor/editor_file_system.h +++ b/editor/editor_file_system.h @@ -60,6 +60,7 @@ class EditorFileSystemDirectory : public Object { bool verified; //used for checking changes String script_class_name; String script_class_extends; + String script_class_icon_path; }; struct FileInfoSort { @@ -90,6 +91,7 @@ public: bool get_file_import_is_valid(int p_idx) const; String get_file_script_class_name(int p_idx) const; //used for scripts String get_file_script_class_extends(int p_idx) const; //used for scripts + String get_file_script_class_icon_path(int p_idx) const; //used for scripts EditorFileSystemDirectory *get_parent(); @@ -163,6 +165,7 @@ class EditorFileSystem : public Node { bool import_valid; String script_class_name; String script_class_extends; + String script_class_icon_path; }; HashMap<String, FileCache> file_cache; @@ -225,7 +228,7 @@ class EditorFileSystem : public Node { volatile bool update_script_classes_queued; void _queue_update_script_classes(); - String _get_global_script_class(const String &p_type, const String &p_path, String *r_extends) const; + String _get_global_script_class(const String &p_type, const String &p_path, String *r_extends, String *r_icon_path) const; protected: void _notification(int p_what); diff --git a/editor/editor_help.cpp b/editor/editor_help.cpp index b7a5f67870..65c41ef579 100644 --- a/editor/editor_help.cpp +++ b/editor/editor_help.cpp @@ -253,7 +253,8 @@ void EditorHelpSearch::_notification(int p_what) { if (p_what == NOTIFICATION_ENTER_TREE) { //_update_icons - search_box->add_icon_override("right_icon", get_icon("Search", "EditorIcons")); + search_box->set_right_icon(get_icon("Search", "EditorIcons")); + search_box->set_clear_button_enabled(true); connect("confirmed", this, "_confirmed"); _update_search(); @@ -267,7 +268,8 @@ void EditorHelpSearch::_notification(int p_what) { } else if (p_what == EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED) { //_update_icons - search_box->add_icon_override("right_icon", get_icon("Search", "EditorIcons")); + search_box->set_right_icon(get_icon("Search", "EditorIcons")); + search_box->set_clear_button_enabled(true); } else if (p_what == NOTIFICATION_PROCESS) { if (search.is_valid()) { @@ -381,7 +383,8 @@ void EditorHelpIndex::_notification(int p_what) { if (p_what == NOTIFICATION_ENTER_TREE) { //_update_icons - search_box->add_icon_override("right_icon", get_icon("Search", "EditorIcons")); + search_box->set_right_icon(get_icon("Search", "EditorIcons")); + search_box->set_clear_button_enabled(true); _update_class_list(); connect("confirmed", this, "_tree_item_selected"); @@ -392,7 +395,8 @@ void EditorHelpIndex::_notification(int p_what) { } else if (p_what == EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED) { //_update_icons - search_box->add_icon_override("right_icon", get_icon("Search", "EditorIcons")); + search_box->set_right_icon(get_icon("Search", "EditorIcons")); + search_box->set_clear_button_enabled(true); } } @@ -541,6 +545,7 @@ void EditorHelp::_class_desc_select(const String &p_select) { String class_name; if (select.find(".") != -1) { class_name = select.get_slice(".", 0); + select = select.get_slice(".", 1); } else { class_name = "@GlobalScope"; } @@ -713,16 +718,22 @@ Error EditorHelp::_goto_desc(const String &p_class, int p_vscr) { if (p_class == edited_class) return OK; //already there + edited_class = p_class; + _update_doc(); + return OK; +} + +void EditorHelp::_update_doc() { + scroll_locked = true; class_desc->clear(); method_line.clear(); section_line.clear(); - edited_class = p_class; _init_colors(); - DocData::ClassDoc cd = doc->class_list[p_class]; //make a copy, so we can sort without worrying + DocData::ClassDoc cd = doc->class_list[edited_class]; //make a copy, so we can sort without worrying Ref<Font> doc_font = get_font("doc", "EditorFonts"); Ref<Font> doc_title_font = get_font("doc_title", "EditorFonts"); @@ -734,7 +745,7 @@ Error EditorHelp::_goto_desc(const String &p_class, int p_vscr) { class_desc->push_color(title_color); class_desc->add_text(TTR("Class:") + " "); class_desc->push_color(headline_color); - _add_text(p_class); + _add_text(edited_class); class_desc->pop(); class_desc->pop(); class_desc->pop(); @@ -1453,8 +1464,6 @@ Error EditorHelp::_goto_desc(const String &p_class, int p_vscr) { } scroll_locked = false; - - return OK; } void EditorHelp::_request_help(const String &p_string) { @@ -1751,9 +1760,6 @@ void EditorHelp::_add_text(const String &p_bbcode) { _add_text_to_rt(p_bbcode, class_desc); } -void EditorHelp::_update_doc() { -} - void EditorHelp::generate_doc() { doc = memnew(DocData); @@ -1775,7 +1781,8 @@ void EditorHelp::_notification(int p_what) { case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: { - class_desc->add_color_override("selection_color", get_color("text_editor/theme/selection_color", "Editor")); + class_desc->add_color_override("selection_color", EditorSettings::get_singleton()->get("text_editor/theme/selection_color")); + _update_doc(); } break; @@ -1856,7 +1863,7 @@ EditorHelp::EditorHelp() { class_desc = memnew(RichTextLabel); add_child(class_desc); class_desc->set_v_size_flags(SIZE_EXPAND_FILL); - class_desc->add_color_override("selection_color", get_color("text_editor/theme/selection_color", "Editor")); + class_desc->add_color_override("selection_color", EditorSettings::get_singleton()->get("text_editor/theme/selection_color")); class_desc->connect("meta_clicked", this, "_class_desc_select"); class_desc->connect("gui_input", this, "_class_desc_input"); @@ -1922,7 +1929,7 @@ void EditorHelpBit::_notification(int p_what) { switch (p_what) { case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: { - rich_text->add_color_override("selection_color", get_color("text_editor/theme/selection_color", "Editor")); + rich_text->add_color_override("selection_color", EditorSettings::get_singleton()->get("text_editor/theme/selection_color")); } break; default: break; @@ -1941,7 +1948,7 @@ EditorHelpBit::EditorHelpBit() { add_child(rich_text); //rich_text->set_anchors_and_margins_preset(Control::PRESET_WIDE); rich_text->connect("meta_clicked", this, "_meta_clicked"); - rich_text->add_color_override("selection_color", get_color("text_editor/theme/selection_color", "Editor")); + rich_text->add_color_override("selection_color", EditorSettings::get_singleton()->get("text_editor/theme/selection_color")); rich_text->set_override_selected_font_color(false); set_custom_minimum_size(Size2(0, 70 * EDSCALE)); } diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp index 99a2b2aa67..852e1930d2 100644 --- a/editor/editor_inspector.cpp +++ b/editor/editor_inspector.cpp @@ -1533,9 +1533,10 @@ void EditorInspector::update_tree() { if (capitalize_paths) path_name = path_name.capitalize(); + Color c = sscolor; c.a /= level; - section->setup(path_name, acc_path, object, c, use_folding); + section->setup(acc_path, path_name, object, c, use_folding); item_path[acc_path] = section->get_vbox(); } @@ -2221,7 +2222,7 @@ EditorInspector::EditorInspector() { show_categories = false; hide_script = true; use_doc_hints = false; - capitalize_paths = false; + capitalize_paths = true; use_filter = false; autoclear = false; changing = 0; diff --git a/editor/editor_log.cpp b/editor/editor_log.cpp index 158eedfb0f..b3ec717d85 100644 --- a/editor/editor_log.cpp +++ b/editor/editor_log.cpp @@ -54,7 +54,11 @@ void EditorLog::_error_handler(void *p_self, const char *p_func, const char *p_f self->emit_signal("show_request"); */ - self->add_message(err_str, true); + if (p_type == ERR_HANDLER_WARNING) { + self->add_message(err_str, MSG_TYPE_WARNING); + } else { + self->add_message(err_str, MSG_TYPE_ERROR); + } } void EditorLog::_notification(int p_what) { @@ -95,22 +99,32 @@ void EditorLog::clear() { _clear_request(); } -void EditorLog::add_message(const String &p_msg, bool p_error) { +void EditorLog::add_message(const String &p_msg, MessageType p_type) { log->add_newline(); - if (p_error) { - log->push_color(get_color("error_color", "Editor")); - Ref<Texture> icon = get_icon("Error", "EditorIcons"); - log->add_image(icon); - log->add_text(" "); - tool_button->set_icon(icon); + bool restore = p_type != MSG_TYPE_STD; + switch (p_type) { + case MSG_TYPE_ERROR: { + log->push_color(get_color("error_color", "Editor")); + Ref<Texture> icon = get_icon("Error", "EditorIcons"); + log->add_image(icon); + log->add_text(" "); + tool_button->set_icon(icon); + } break; + case MSG_TYPE_WARNING: { + log->push_color(get_color("warning_color", "Editor")); + Ref<Texture> icon = get_icon("Warning", "EditorIcons"); + log->add_image(icon); + log->add_text(" "); + tool_button->set_icon(icon); + } break; } log->add_text(p_msg); //button->set_text(p_msg); - if (p_error) + if (restore) log->pop(); } diff --git a/editor/editor_log.h b/editor/editor_log.h index f9bc82de7d..8d0310d914 100644 --- a/editor/editor_log.h +++ b/editor/editor_log.h @@ -42,6 +42,7 @@ #include "scene/gui/panel_container.h" #include "scene/gui/texture_rect.h" #include "scene/gui/tool_button.h" + class EditorLog : public VBoxContainer { GDCLASS(EditorLog, VBoxContainer); @@ -68,7 +69,13 @@ protected: void _notification(int p_what); public: - void add_message(const String &p_msg, bool p_error = false); + enum MessageType { + MSG_TYPE_STD, + MSG_TYPE_ERROR, + MSG_TYPE_WARNING + }; + + void add_message(const String &p_msg, MessageType p_type = MSG_TYPE_STD); void set_tool_button(ToolButton *p_tool_button); void deinit(); diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index 81b7a66361..975c7f8345 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -74,6 +74,7 @@ #include "editor/plugins/animation_player_editor_plugin.h" #include "editor/plugins/animation_state_machine_editor.h" #include "editor/plugins/animation_tree_editor_plugin.h" +#include "editor/plugins/animation_tree_player_editor_plugin.h" #include "editor/plugins/asset_library_editor_plugin.h" #include "editor/plugins/audio_stream_editor_plugin.h" #include "editor/plugins/baked_lightmap_editor_plugin.h" @@ -411,6 +412,18 @@ void EditorNode::_notification(int p_what) { } } +void EditorNode::_on_plugin_ready(Object *p_script, const String &p_activate_name) { + Ref<Script> script = Object::cast_to<Script>(p_script); + if (script.is_null()) + return; + if (p_activate_name.length()) { + set_addon_plugin_enabled(p_activate_name, true); + } + project_settings->update_plugins(); + project_settings->hide(); + push_item(script.operator->()); +} + void EditorNode::_fs_changed() { for (Set<FileDialog *>::Element *E = file_dialogs.front(); E; E = E->next()) { @@ -2547,6 +2560,7 @@ void EditorNode::set_addon_plugin_enabled(const String &p_addon, bool p_enabled) EditorPlugin *ep = memnew(EditorPlugin); ep->set_script(script.get_ref_ptr()); + ep->set_dir_cache(p_addon); plugin_addons[p_addon] = ep; add_editor_plugin(ep); @@ -4548,6 +4562,8 @@ void EditorNode::_bind_methods() { ClassDB::bind_method(D_METHOD("_resources_reimported"), &EditorNode::_resources_reimported); ClassDB::bind_method(D_METHOD("_bottom_panel_raise_toggled"), &EditorNode::_bottom_panel_raise_toggled); + ClassDB::bind_method(D_METHOD("_on_plugin_ready"), &EditorNode::_on_plugin_ready); + ClassDB::bind_method(D_METHOD("_video_driver_selected"), &EditorNode::_video_driver_selected); ADD_SIGNAL(MethodInfo("play_pressed")); @@ -4566,7 +4582,7 @@ static Node *_resource_get_edited_scene() { void EditorNode::_print_handler(void *p_this, const String &p_string, bool p_error) { EditorNode *en = (EditorNode *)p_this; - en->log->add_message(p_string, p_error); + en->log->add_message(p_string, p_error ? EditorLog::MSG_TYPE_ERROR : EditorLog::MSG_TYPE_STD); } EditorNode::EditorNode() { @@ -4776,7 +4792,7 @@ EditorNode::EditorNode() { EDITOR_DEF("interface/inspector/horizontal_vector2_editing", false); EDITOR_DEF("interface/inspector/horizontal_vector3_editing", true); EDITOR_DEF("interface/inspector/open_resources_in_current_inspector", true); - EDITOR_DEF("interface/inspector/resources_types_to_open_in_new_inspector", "SpatialMaterial"); + EDITOR_DEF("interface/inspector/resources_types_to_open_in_new_inspector", "SpatialMaterial,Script"); EDITOR_DEF("run/auto_save/save_before_running", true); theme_base = memnew(Control); @@ -5134,6 +5150,10 @@ EditorNode::EditorNode() { p->connect("id_pressed", this, "_menu_option"); p->add_item(TTR("Export"), FILE_EXPORT_PROJECT); + plugin_config_dialog = memnew(PluginConfigDialog); + plugin_config_dialog->connect("plugin_ready", this, "_on_plugin_ready"); + gui_base->add_child(plugin_config_dialog); + tool_menu = memnew(PopupMenu); tool_menu->set_name("Tools"); tool_menu->connect("index_pressed", this, "_tool_menu_option"); @@ -5409,7 +5429,7 @@ EditorNode::EditorNode() { } filesystem_dock = memnew(FileSystemDock(this)); - filesystem_dock->set_display_mode(int(EditorSettings::get_singleton()->get("docks/filesystem/display_mode"))); + filesystem_dock->set_file_list_display_mode(int(EditorSettings::get_singleton()->get("docks/filesystem/display_mode"))); if (use_single_dock_column) { dock_slot[DOCK_SLOT_RIGHT_BL]->add_child(filesystem_dock); @@ -5448,6 +5468,7 @@ EditorNode::EditorNode() { bottom_panel->add_child(bottom_panel_vb); bottom_panel_hb = memnew(HBoxContainer); + bottom_panel_hb->set_custom_minimum_size(Size2(0, 24)); // Adjust for the height of the "Expand Bottom Dock" icon. bottom_panel_vb->add_child(bottom_panel_hb); bottom_panel_hb_editors = memnew(HBoxContainer); @@ -5569,16 +5590,13 @@ EditorNode::EditorNode() { add_editor_plugin(memnew(ShaderEditorPlugin(this))); add_editor_plugin(memnew(VisualShaderEditorPlugin(this))); - add_editor_plugin(memnew(AnimationNodeBlendTreeEditorPlugin(this))); - add_editor_plugin(memnew(AnimationNodeBlendSpace1DEditorPlugin(this))); - add_editor_plugin(memnew(AnimationNodeBlendSpace2DEditorPlugin(this))); - add_editor_plugin(memnew(AnimationNodeStateMachineEditorPlugin(this))); add_editor_plugin(memnew(CameraEditorPlugin(this))); add_editor_plugin(memnew(ThemeEditorPlugin(this))); add_editor_plugin(memnew(MultiMeshEditorPlugin(this))); add_editor_plugin(memnew(MeshInstanceEditorPlugin(this))); add_editor_plugin(memnew(AnimationTreeEditorPlugin(this))); + add_editor_plugin(memnew(AnimationTreePlayerEditorPlugin(this))); add_editor_plugin(memnew(MeshLibraryEditorPlugin(this))); add_editor_plugin(memnew(StyleBoxEditorPlugin(this))); add_editor_plugin(memnew(SpriteEditorPlugin(this))); diff --git a/editor/editor_node.h b/editor/editor_node.h index 85aa37ec7e..5a17ab6ca0 100644 --- a/editor/editor_node.h +++ b/editor/editor_node.h @@ -56,6 +56,7 @@ #include "editor/inspector_dock.h" #include "editor/node_dock.h" #include "editor/pane_drag.h" +#include "editor/plugin_config_dialog.h" #include "editor/progress_dialog.h" #include "editor/project_export.h" #include "editor/project_settings_editor.h" @@ -257,6 +258,8 @@ private: ToolButton *search_button; TextureProgress *audio_vu; + PluginConfigDialog *plugin_config_dialog; + RichTextLabel *load_errors; AcceptDialog *load_error_dialog; @@ -416,6 +419,8 @@ private: void _tool_menu_option(int p_idx); void _update_debug_options(); + void _on_plugin_ready(Object *p_script, const String &p_activate_name); + void _fs_changed(); void _resources_reimported(const Vector<String> &p_resources); void _sources_changed(bool p_exist); diff --git a/editor/editor_plugin.cpp b/editor/editor_plugin.cpp index a926401558..137e710c5c 100644 --- a/editor/editor_plugin.cpp +++ b/editor/editor_plugin.cpp @@ -309,6 +309,12 @@ void EditorPlugin::remove_autoload_singleton(const String &p_name) { EditorNode::get_singleton()->get_project_settings()->get_autoload_settings()->autoload_remove(p_name); } +Ref<ConfigFile> EditorPlugin::get_config() { + Ref<ConfigFile> cf = memnew(ConfigFile); + cf->load(_dir_cache.plus_file("plugin.cfg")); + return cf; +} + ToolButton *EditorPlugin::add_control_to_bottom_panel(Control *p_control, const String &p_title) { ERR_FAIL_NULL_V(p_control, NULL); return EditorNode::get_singleton()->add_bottom_panel_item(p_title, p_control); diff --git a/editor/editor_plugin.h b/editor/editor_plugin.h index c417f487dc..903b82937f 100644 --- a/editor/editor_plugin.h +++ b/editor/editor_plugin.h @@ -114,6 +114,7 @@ class EditorPlugin : public Node { bool force_draw_over_forwarding_enabled; String last_main_screen_name; + String _dir_cache; protected: static void _bind_methods(); @@ -221,6 +222,10 @@ public: void add_autoload_singleton(const String &p_name, const String &p_path); void remove_autoload_singleton(const String &p_name); + void set_dir_cache(const String &p_dir) { _dir_cache = p_dir; } + String get_dir_cache() { return _dir_cache; } + Ref<ConfigFile> get_config(); + EditorPlugin(); virtual ~EditorPlugin(); }; diff --git a/editor/editor_plugin_settings.cpp b/editor/editor_plugin_settings.cpp index ea1e0fe99e..68f8ed6d94 100644 --- a/editor/editor_plugin_settings.cpp +++ b/editor/editor_plugin_settings.cpp @@ -41,6 +41,9 @@ void EditorPluginSettings::_notification(int p_what) { if (p_what == MainLoop::NOTIFICATION_WM_FOCUS_IN) { update_plugins(); + } else if (p_what == Node::NOTIFICATION_READY) { + plugin_config_dialog->connect("plugin_ready", EditorNode::get_singleton(), "_on_plugin_ready"); + plugin_list->connect("button_pressed", this, "_cell_button_pressed"); } } @@ -124,6 +127,7 @@ void EditorPluginSettings::update_plugins() { item->set_range_config(3, 0, 1, 1); item->set_text(3, "Inactive,Active"); item->set_editable(3, true); + item->add_button(4, get_icon("Edit", "EditorIcons"), BUTTON_PLUGIN_EDIT, false, TTR("Edit Plugin")); if (EditorNode::get_singleton()->is_addon_plugin_enabled(d)) { item->set_custom_color(3, get_color("success_color", "Editor")); @@ -164,17 +168,44 @@ void EditorPluginSettings::_plugin_activity_changed() { ti->set_custom_color(3, get_color("disabled_font_color", "Editor")); } +void EditorPluginSettings::_create_clicked() { + plugin_config_dialog->config(""); + plugin_config_dialog->popup_centered(); +} + +void EditorPluginSettings::_cell_button_pressed(Object *p_item, int p_column, int p_id) { + TreeItem *item = Object::cast_to<TreeItem>(p_item); + if (!item) + return; + if (p_id == BUTTON_PLUGIN_EDIT) { + if (p_column == 4) { + String dir = item->get_metadata(0); + plugin_config_dialog->config("res://addons/" + dir + "/plugin.cfg"); + plugin_config_dialog->popup_centered(); + } + } +} + void EditorPluginSettings::_bind_methods() { ClassDB::bind_method("update_plugins", &EditorPluginSettings::update_plugins); + ClassDB::bind_method("_create_clicked", &EditorPluginSettings::_create_clicked); ClassDB::bind_method("_plugin_activity_changed", &EditorPluginSettings::_plugin_activity_changed); + ClassDB::bind_method("_cell_button_pressed", &EditorPluginSettings::_cell_button_pressed); } EditorPluginSettings::EditorPluginSettings() { + plugin_config_dialog = memnew(PluginConfigDialog); + plugin_config_dialog->config(""); + add_child(plugin_config_dialog); + HBoxContainer *title_hb = memnew(HBoxContainer); title_hb->add_child(memnew(Label(TTR("Installed Plugins:")))); title_hb->add_spacer(); + create_plugin = memnew(Button(TTR("Create"))); + create_plugin->connect("pressed", this, "_create_clicked"); + title_hb->add_child(create_plugin); update_list = memnew(Button(TTR("Update"))); update_list->connect("pressed", this, "update_plugins"); title_hb->add_child(update_list); @@ -182,19 +213,22 @@ EditorPluginSettings::EditorPluginSettings() { plugin_list = memnew(Tree); plugin_list->set_v_size_flags(SIZE_EXPAND_FILL); - plugin_list->set_columns(4); + plugin_list->set_columns(5); plugin_list->set_column_titles_visible(true); plugin_list->set_column_title(0, TTR("Name:")); plugin_list->set_column_title(1, TTR("Version:")); plugin_list->set_column_title(2, TTR("Author:")); plugin_list->set_column_title(3, TTR("Status:")); + plugin_list->set_column_title(4, TTR("Edit:")); plugin_list->set_column_expand(0, true); plugin_list->set_column_expand(1, false); plugin_list->set_column_expand(2, false); plugin_list->set_column_expand(3, false); + plugin_list->set_column_expand(4, false); plugin_list->set_column_min_width(1, 100 * EDSCALE); plugin_list->set_column_min_width(2, 250 * EDSCALE); plugin_list->set_column_min_width(3, 80 * EDSCALE); + plugin_list->set_column_min_width(4, 40 * EDSCALE); plugin_list->set_hide_root(true); plugin_list->connect("item_edited", this, "_plugin_activity_changed"); diff --git a/editor/editor_plugin_settings.h b/editor/editor_plugin_settings.h index aacbd05dd4..194fac6b92 100644 --- a/editor/editor_plugin_settings.h +++ b/editor/editor_plugin_settings.h @@ -31,6 +31,7 @@ #ifndef EDITORPLUGINSETTINGS_H #define EDITORPLUGINSETTINGS_H +#include "editor/plugin_config_dialog.h" #include "editor_data.h" #include "property_editor.h" #include "scene/gui/dialogs.h" @@ -40,11 +41,19 @@ class EditorPluginSettings : public VBoxContainer { GDCLASS(EditorPluginSettings, VBoxContainer); + enum { + BUTTON_PLUGIN_EDIT + }; + + PluginConfigDialog *plugin_config_dialog; + Button *create_plugin; Button *update_list; Tree *plugin_list; bool updating; void _plugin_activity_changed(); + void _create_clicked(); + void _cell_button_pressed(Object *p_item, int p_column, int p_id); protected: void _notification(int p_what); diff --git a/editor/editor_properties.cpp b/editor/editor_properties.cpp index 83a3662f21..5f1e7273e5 100644 --- a/editor/editor_properties.cpp +++ b/editor/editor_properties.cpp @@ -46,6 +46,17 @@ EditorPropertyNil::EditorPropertyNil() { } ///////////////////// TEXT ///////////////////////// + +void EditorPropertyText::_text_entered(const String &p_string) { + if (updating) + return; + + if (text->has_focus()) { + text->release_focus(); + _text_changed(p_string); + } +} + void EditorPropertyText::_text_changed(const String &p_string) { if (updating) return; @@ -61,9 +72,14 @@ void EditorPropertyText::update_property() { updating = false; } +void EditorPropertyText::set_placeholder(const String &p_string) { + text->set_placeholder(p_string); +} + void EditorPropertyText::_bind_methods() { ClassDB::bind_method(D_METHOD("_text_changed", "txt"), &EditorPropertyText::_text_changed); + ClassDB::bind_method(D_METHOD("_text_entered", "txt"), &EditorPropertyText::_text_entered); } EditorPropertyText::EditorPropertyText() { @@ -71,6 +87,8 @@ EditorPropertyText::EditorPropertyText() { add_child(text); add_focusable(text); text->connect("text_changed", this, "_text_changed"); + text->connect("text_entered", this, "_text_entered"); + updating = false; } @@ -82,7 +100,6 @@ void EditorPropertyMultilineText::_big_text_changed() { } void EditorPropertyMultilineText::_text_changed() { - emit_signal("property_changed", get_edited_property(), text->get_text(), true); } @@ -1721,12 +1738,18 @@ EditorPropertyTransform::EditorPropertyTransform() { void EditorPropertyColor::_color_changed(const Color &p_color) { - emit_signal("property_changed", get_edited_property(), p_color); + emit_signal("property_changed", get_edited_property(), p_color, true); +} + +void EditorPropertyColor::_popup_closed() { + + emit_signal("property_changed", get_edited_property(), picker->get_pick_color(), false); } void EditorPropertyColor::_bind_methods() { ClassDB::bind_method(D_METHOD("_color_changed"), &EditorPropertyColor::_color_changed); + ClassDB::bind_method(D_METHOD("_popup_closed"), &EditorPropertyColor::_popup_closed); } void EditorPropertyColor::update_property() { @@ -1744,6 +1767,7 @@ EditorPropertyColor::EditorPropertyColor() { add_child(picker); picker->set_flat(true); picker->connect("color_changed", this, "_color_changed"); + picker->connect("popup_closed", this, "_popup_closed"); } ////////////// NODE PATH ////////////////////// @@ -2038,7 +2062,7 @@ void EditorPropertyResource::_menu_option(int p_which) { ERR_BREAK(!resp); if (get_edited_object() && base_type != String() && base_type == "Script") { //make visual script the right type - res->call("set_instance_base_type", get_edited_object()->get_class()); + resp->call("set_instance_base_type", get_edited_object()->get_class()); } res = Ref<Resource>(resp); @@ -2240,7 +2264,7 @@ void EditorPropertyResource::_sub_inspector_object_id_selected(int p_id) { void EditorPropertyResource::_open_editor_pressed() { RES res = get_edited_object()->get(get_edited_property()); if (res.is_valid()) { - EditorNode::get_singleton()->edit_resource(res.ptr()); + EditorNode::get_singleton()->edit_item(res.ptr()); } } @@ -2763,6 +2787,9 @@ bool EditorInspectorDefaultPlugin::parse_property(Object *p_object, Variant::Typ } else { EditorPropertyText *editor = memnew(EditorPropertyText); + if (p_hint == PROPERTY_HINT_PLACEHOLDER_TEXT) { + editor->set_placeholder(p_hint_text); + } add_property_editor(p_path, editor); } } break; diff --git a/editor/editor_properties.h b/editor/editor_properties.h index ccd73d2539..5726ccfa41 100644 --- a/editor/editor_properties.h +++ b/editor/editor_properties.h @@ -54,12 +54,14 @@ class EditorPropertyText : public EditorProperty { bool updating; void _text_changed(const String &p_string); + void _text_entered(const String &p_string); protected: static void _bind_methods(); public: virtual void update_property(); + void set_placeholder(const String &p_string); EditorPropertyText(); }; @@ -475,6 +477,7 @@ class EditorPropertyColor : public EditorProperty { GDCLASS(EditorPropertyColor, EditorProperty) ColorPickerButton *picker; void _color_changed(const Color &p_color); + void _popup_closed(); protected: static void _bind_methods(); diff --git a/editor/editor_properties_array_dict.cpp b/editor/editor_properties_array_dict.cpp index 2bd28170e7..23dbb026dd 100644 --- a/editor/editor_properties_array_dict.cpp +++ b/editor/editor_properties_array_dict.cpp @@ -125,13 +125,13 @@ EditorPropertyDictionaryObject::EditorPropertyDictionaryObject() { ///////////////////// ARRAY /////////////////////////// -void EditorPropertyArray::_property_changed(const String &p_prop, Variant p_value) { +void EditorPropertyArray::_property_changed(const String &p_prop, Variant p_value, bool changing) { if (p_prop.begins_with("indices")) { int idx = p_prop.get_slice("/", 1).to_int(); Variant array = object->get_array(); array.set(idx, p_value); - emit_signal("property_changed", get_edited_property(), array); + emit_signal("property_changed", get_edited_property(), array, true); if (array.get_type() == Variant::ARRAY) { array = array.call("duplicate"); //dupe, so undo/redo works better @@ -210,8 +210,8 @@ void EditorPropertyArray::update_property() { default: {} } - if (!array.is_array()) { - edit->set_text(arrtype + "[" + Variant::get_type_name(array.get_type()) + "]"); + if (array.get_type() == Variant::NIL) { + edit->set_text(String("(Nil) ") + arrtype); edit->set_pressed(false); if (vbox) { memdelete(vbox); @@ -219,7 +219,7 @@ void EditorPropertyArray::update_property() { return; } - edit->set_text(arrtype + "[" + itos(array.call("size")) + "]"); + edit->set_text(arrtype + "(size " + itos(array.call("size")) + ")"); #ifdef TOOLS_ENABLED @@ -544,7 +544,7 @@ void EditorPropertyArray::_bind_methods() { ClassDB::bind_method("_edit_pressed", &EditorPropertyArray::_edit_pressed); ClassDB::bind_method("_page_changed", &EditorPropertyArray::_page_changed); ClassDB::bind_method("_length_changed", &EditorPropertyArray::_length_changed); - ClassDB::bind_method("_property_changed", &EditorPropertyArray::_property_changed); + ClassDB::bind_method("_property_changed", &EditorPropertyArray::_property_changed, DEFVAL(false)); ClassDB::bind_method("_change_type", &EditorPropertyArray::_change_type); ClassDB::bind_method("_change_type_menu", &EditorPropertyArray::_change_type_menu); } @@ -579,7 +579,7 @@ EditorPropertyArray::EditorPropertyArray() { ///////////////////// DICTIONARY /////////////////////////// -void EditorPropertyDictionary::_property_changed(const String &p_prop, Variant p_value) { +void EditorPropertyDictionary::_property_changed(const String &p_prop, Variant p_value, bool changing) { if (p_prop == "new_item_key") { @@ -593,7 +593,7 @@ void EditorPropertyDictionary::_property_changed(const String &p_prop, Variant p Variant key = dict.get_key_at_index(idx); dict[key] = p_value; - emit_signal("property_changed", get_edited_property(), dict); + emit_signal("property_changed", get_edited_property(), dict, true); dict = dict.duplicate(); //dupe, so undo/redo works better object->set_dict(dict); @@ -613,7 +613,13 @@ void EditorPropertyDictionary::_change_type(Object *p_button, int p_index) { void EditorPropertyDictionary::_add_key_value() { + // Do not allow nil as valid key. I experienced errors with this + if (object->get_new_item_key().get_type() == Variant::NIL) { + return; + } + Dictionary dict = object->get_dict(); + dict[object->get_new_item_key()] = object->get_new_item_value(); object->set_new_item_key(Variant()); object->set_new_item_value(Variant()); @@ -663,9 +669,20 @@ void EditorPropertyDictionary::_change_type_menu(int p_index) { void EditorPropertyDictionary::update_property() { - Dictionary dict = get_edited_object()->get(get_edited_property()); + Variant updated_val = get_edited_object()->get(get_edited_property()); + + if (updated_val.get_type() == Variant::NIL) { + edit->set_text("Dictionary (Nil)"); //This provides symmetry with the array property. + edit->set_pressed(false); + if (vbox) { + memdelete(vbox); + } + return; + } + + Dictionary dict = updated_val; - edit->set_text("Dict[" + itos(dict.size()) + "]"); + edit->set_text("Dictionary (size " + itos(dict.size()) + ")"); #ifdef TOOLS_ENABLED @@ -695,9 +712,9 @@ void EditorPropertyDictionary::update_property() { page->set_h_size_flags(SIZE_EXPAND_FILL); page->connect("value_changed", this, "_page_changed"); } else { - //bye bye children of the box - while (vbox->get_child_count() > 1) { - memdelete(vbox->get_child(1)); + // Queue childs for deletion, delete immediately might cause errors. + for (size_t i = 1; i < vbox->get_child_count(); i++) { + vbox->get_child(i)->queue_delete(); } } @@ -941,10 +958,10 @@ void EditorPropertyDictionary::update_property() { prop->update_property(); if (i == amount + 1) { - Button *add_item = memnew(Button); - add_item->set_text(TTR("Add Key/Value Pair")); - add_vbox->add_child(add_item); - add_item->connect("pressed", this, "_add_key_value"); + Button *butt_add_item = memnew(Button); + butt_add_item->set_text(TTR("Add Key/Value Pair")); + butt_add_item->connect("pressed", this, "_add_key_value"); + add_vbox->add_child(butt_add_item); } } @@ -965,8 +982,16 @@ void EditorPropertyDictionary::_notification(int p_what) { if (p_what == NOTIFICATION_ENTER_TREE || p_what == NOTIFICATION_THEME_CHANGED) { } } + void EditorPropertyDictionary::_edit_pressed() { + Variant prop_val = get_edited_object()->get(get_edited_property()); + if (prop_val.get_type() == Variant::NIL) { + Variant::CallError ce; + prop_val = Variant::construct(Variant::DICTIONARY, NULL, 0, ce); + get_edited_object()->set(get_edited_property(), prop_val); + } + get_edited_object()->editor_set_section_unfold(get_edited_property(), edit->is_pressed()); update_property(); } @@ -981,7 +1006,7 @@ void EditorPropertyDictionary::_page_changed(double p_page) { void EditorPropertyDictionary::_bind_methods() { ClassDB::bind_method("_edit_pressed", &EditorPropertyDictionary::_edit_pressed); ClassDB::bind_method("_page_changed", &EditorPropertyDictionary::_page_changed); - ClassDB::bind_method("_property_changed", &EditorPropertyDictionary::_property_changed); + ClassDB::bind_method("_property_changed", &EditorPropertyDictionary::_property_changed, DEFVAL(false)); ClassDB::bind_method("_change_type", &EditorPropertyDictionary::_change_type); ClassDB::bind_method("_change_type_menu", &EditorPropertyDictionary::_change_type_menu); ClassDB::bind_method("_add_key_value", &EditorPropertyDictionary::_add_key_value); diff --git a/editor/editor_properties_array_dict.h b/editor/editor_properties_array_dict.h index 75c67d280d..a8ddb02e9d 100644 --- a/editor/editor_properties_array_dict.h +++ b/editor/editor_properties_array_dict.h @@ -67,7 +67,7 @@ class EditorPropertyArray : public EditorProperty { void _page_changed(double p_page); void _length_changed(double p_page); void _edit_pressed(); - void _property_changed(const String &p_prop, Variant p_value); + void _property_changed(const String &p_prop, Variant p_value, bool changing = false); void _change_type(Object *p_button, int p_index); void _change_type_menu(int p_index); @@ -99,7 +99,7 @@ class EditorPropertyDictionary : public EditorProperty { void _page_changed(double p_page); void _edit_pressed(); - void _property_changed(const String &p_prop, Variant p_value); + void _property_changed(const String &p_prop, Variant p_value, bool changing = false); void _change_type(Object *p_button, int p_index); void _change_type_menu(int p_index); diff --git a/editor/editor_settings.cpp b/editor/editor_settings.cpp index 02e121b5f1..85186440ab 100644 --- a/editor/editor_settings.cpp +++ b/editor/editor_settings.cpp @@ -357,6 +357,7 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) { hints["text_editor/theme/color_theme"] = PropertyInfo(Variant::STRING, "text_editor/theme/color_theme", PROPERTY_HINT_ENUM, "Adaptive,Default,Custom"); _initial_set("text_editor/theme/line_spacing", 4); + _initial_set("text_editor/theme/selection_color", Color::html("40808080")); _load_default_text_editor_theme(); @@ -396,7 +397,7 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) { _initial_set("text_editor/cursor/block_caret", false); _initial_set("text_editor/cursor/caret_blink", true); - _initial_set("text_editor/cursor/caret_blink_speed", 0.65); + _initial_set("text_editor/cursor/caret_blink_speed", 0.5); hints["text_editor/cursor/caret_blink_speed"] = PropertyInfo(Variant::REAL, "text_editor/cursor/caret_blink_speed", PROPERTY_HINT_RANGE, "0.1, 10, 0.01"); _initial_set("text_editor/cursor/right_click_moves_caret", true); @@ -473,6 +474,7 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) { hints["editors/3d/freelook/freelook_modifier_speed_factor"] = PropertyInfo(Variant::REAL, "editors/3d/freelook/freelook_modifier_speed_factor", PROPERTY_HINT_RANGE, "0.0, 10.0, 0.1"); _initial_set("editors/3d/freelook/freelook_speed_zoom_link", false); + _initial_set("editors/2d/grid_color", Color(1.0, 1.0, 1.0, 0.07)); _initial_set("editors/2d/guides_color", Color(0.6, 0.0, 0.8)); _initial_set("editors/2d/bone_width", 5); _initial_set("editors/2d/bone_color1", Color(1.0, 1.0, 1.0, 0.9)); @@ -511,6 +513,8 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) { _initial_set("filesystem/file_dialog/thumbnail_size", 64); hints["filesystem/file_dialog/thumbnail_size"] = PropertyInfo(Variant::INT, "filesystem/file_dialog/thumbnail_size", PROPERTY_HINT_RANGE, "32,128,16"); + _initial_set("docks/filesystem/disable_split", false); + _initial_set("docks/filesystem/split_mode_minimum_height", 600); _initial_set("docks/filesystem/display_mode", 0); hints["docks/filesystem/display_mode"] = PropertyInfo(Variant::INT, "docks/filesystem/display_mode", PROPERTY_HINT_ENUM, "Thumbnails,List"); _initial_set("docks/filesystem/thumbnail_size", 64); @@ -755,7 +759,7 @@ void EditorSettings::create() { } if (dir->change_dir(data_dir) != OK) { - dir->make_dir(data_dir); + dir->make_dir_recursive(data_dir); if (dir->change_dir(data_dir) != OK) { ERR_PRINT("Cannot create data directory!"); memdelete(dir); @@ -771,14 +775,8 @@ void EditorSettings::create() { // Validate/create cache dir - if (dir->change_dir(cache_path) != OK) { - ERR_PRINT("Cannot find path for cache directory!"); - memdelete(dir); - goto fail; - } - if (dir->change_dir(cache_dir) != OK) { - dir->make_dir(cache_dir); + dir->make_dir_recursive(cache_dir); if (dir->change_dir(cache_dir) != OK) { ERR_PRINT("Cannot create cache directory!"); memdelete(dir); @@ -788,14 +786,8 @@ void EditorSettings::create() { // Validate/create config dir and subdirectories - if (dir->change_dir(config_path) != OK) { - ERR_PRINT("Cannot find path for config directory!"); - memdelete(dir); - goto fail; - } - if (dir->change_dir(config_dir) != OK) { - dir->make_dir(config_dir); + dir->make_dir_recursive(config_dir); if (dir->change_dir(config_dir) != OK) { ERR_PRINT("Cannot create config directory!"); memdelete(dir); diff --git a/editor/editor_themes.cpp b/editor/editor_themes.cpp index 0a22026591..50d71f1c98 100644 --- a/editor/editor_themes.cpp +++ b/editor/editor_themes.cpp @@ -173,9 +173,9 @@ void editor_register_and_generate_icons(Ref<Theme> p_theme, bool p_dark_theme = const Color error_color = p_theme->get_color("error_color", "Editor"); const Color success_color = p_theme->get_color("success_color", "Editor"); const Color warning_color = p_theme->get_color("warning_color", "Editor"); - dark_icon_color_dictionary[Color::html("#ff5d5d")] = error_color; + dark_icon_color_dictionary[Color::html("#ff0000")] = error_color; dark_icon_color_dictionary[Color::html("#45ff8b")] = success_color; - dark_icon_color_dictionary[Color::html("#ffdd65")] = warning_color; + dark_icon_color_dictionary[Color::html("#dbab09")] = warning_color; List<String> exceptions; exceptions.push_back("EditorPivot"); @@ -365,13 +365,13 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { theme->set_color("mono_color", "Editor", mono_color); Color success_color = accent_color.linear_interpolate(Color(0.2, 1, 0.2), 0.6) * 1.2; - Color warning_color = accent_color.linear_interpolate(Color(1, 1, 0), 0.7) * 1.2; + Color warning_color = accent_color.linear_interpolate(Color(1, 1, 0), 0.7) * 1.0; Color error_color = accent_color.linear_interpolate(Color(1, 0, 0), 0.8) * 1.7; Color property_color = font_color.linear_interpolate(Color(0.5, 0.5, 0.5), 0.5); if (!dark_theme) { // yellow on white themes is a P.I.T.A. - warning_color = accent_color.linear_interpolate(Color(1, 0.8, 0), 0.9); + warning_color = accent_color.linear_interpolate(Color(0.9, 0.7, 0), 0.9); warning_color = warning_color.linear_interpolate(mono_color, 0.2); success_color = success_color.linear_interpolate(mono_color, 0.2); error_color = error_color.linear_interpolate(mono_color, 0.2); @@ -381,12 +381,6 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { theme->set_color("error_color", "Editor", error_color); theme->set_color("property_color", "Editor", property_color); - // 2d grid color - const Color grid_minor_color = mono_color * Color(1.0, 1.0, 1.0, 0.07); - const Color grid_major_color = Color(font_color_disabled.r, font_color_disabled.g, font_color_disabled.b, 0.15); - theme->set_color("grid_major_color", "Editor", grid_major_color); - theme->set_color("grid_minor_color", "Editor", grid_minor_color); - const int thumb_size = EDITOR_DEF("filesystem/file_dialog/thumbnail_size", 64); theme->set_constant("scale", "Editor", EDSCALE); theme->set_constant("thumb_size", "Editor", thumb_size); @@ -828,6 +822,8 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { theme->set_color("font_color", "LineEdit", font_color); theme->set_color("cursor_color", "LineEdit", font_color); theme->set_color("selection_color", "LineEdit", font_color_selection); + theme->set_color("clear_button_color", "LineEdit", font_color); + theme->set_color("clear_button_color_pressed", "LineEdit", accent_color); // TextEdit theme->set_stylebox("normal", "TextEdit", style_widget); @@ -969,8 +965,8 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { // GraphEdit theme->set_stylebox("bg", "GraphEdit", style_tree_bg); - theme->set_color("grid_major", "GraphEdit", grid_major_color); - theme->set_color("grid_minor", "GraphEdit", grid_minor_color); + theme->set_color("grid_major", "GraphEdit", Color(1.0, 1.0, 1.0, 0.15)); + theme->set_color("grid_minor", "GraphEdit", Color(1.0, 1.0, 1.0, 0.07)); theme->set_color("activity", "GraphEdit", accent_color); theme->set_icon("minus", "GraphEdit", theme->get_icon("ZoomLess", "EditorIcons")); theme->set_icon("more", "GraphEdit", theme->get_icon("ZoomMore", "EditorIcons")); diff --git a/editor/export_template_manager.cpp b/editor/export_template_manager.cpp index 931785333f..6c9d1568fa 100644 --- a/editor/export_template_manager.cpp +++ b/editor/export_template_manager.cpp @@ -178,7 +178,7 @@ void ExportTemplateManager::_uninstall_template_confirm() { _update_template_list(); } -void ExportTemplateManager::_install_from_file(const String &p_file, bool p_use_progress) { +bool ExportTemplateManager::_install_from_file(const String &p_file, bool p_use_progress) { FileAccess *fa = NULL; zlib_filefunc_def io = zipio_create_io_from_file(&fa); @@ -187,7 +187,7 @@ void ExportTemplateManager::_install_from_file(const String &p_file, bool p_use_ if (!pkg) { EditorNode::get_singleton()->show_warning(TTR("Can't open export templates zip.")); - return; + return false; } int ret = unzGoToFirstFile(pkg); @@ -221,7 +221,7 @@ void ExportTemplateManager::_install_from_file(const String &p_file, bool p_use_ if (data_str.get_slice_count(".") < 3) { EditorNode::get_singleton()->show_warning(vformat(TTR("Invalid version.txt format inside templates: %s."), data_str)); unzClose(pkg); - return; + return false; } version = data_str; @@ -237,7 +237,7 @@ void ExportTemplateManager::_install_from_file(const String &p_file, bool p_use_ if (version == String()) { EditorNode::get_singleton()->show_warning(TTR("No version.txt found inside templates.")); unzClose(pkg); - return; + return false; } String template_path = EditorSettings::get_singleton()->get_templates_dir().plus_file(version); @@ -247,7 +247,7 @@ void ExportTemplateManager::_install_from_file(const String &p_file, bool p_use_ if (err != OK) { EditorNode::get_singleton()->show_warning(TTR("Error creating path for templates:") + "\n" + template_path); unzClose(pkg); - return; + return false; } memdelete(d); @@ -310,6 +310,8 @@ void ExportTemplateManager::_install_from_file(const String &p_file, bool p_use_ unzClose(pkg); _update_template_list(); + + return true; } void ExportTemplateManager::popup_manager() { @@ -401,7 +403,15 @@ void ExportTemplateManager::_http_download_templates_completed(int p_status, int String path = download_templates->get_download_file(); template_list_state->set_text(TTR("Download Complete.")); template_downloader->hide(); - _install_from_file(path, false); + int ret = _install_from_file(path, false); + if (ret) { + Error err = OS::get_singleton()->move_to_trash(path); + if (err != OK) { + EditorNode::get_singleton()->add_io_error(TTR("Cannot remove:") + "\n" + path + "\n"); + } + } else { + WARN_PRINTS(vformat(TTR("Templates installation failed. The problematic templates archives can be found at '%s'."), path)); + } } } break; } diff --git a/editor/export_template_manager.h b/editor/export_template_manager.h index 54a645c69f..bd43fe5dc5 100644 --- a/editor/export_template_manager.h +++ b/editor/export_template_manager.h @@ -70,7 +70,7 @@ class ExportTemplateManager : public ConfirmationDialog { void _uninstall_template_confirm(); virtual void ok_pressed(); - void _install_from_file(const String &p_file, bool p_use_progress = true); + bool _install_from_file(const String &p_file, bool p_use_progress = true); void _http_download_mirror_completed(int p_status, int p_code, const PoolStringArray &headers, const PoolByteArray &p_data); void _http_download_templates_completed(int p_status, int p_code, const PoolStringArray &headers, const PoolByteArray &p_data); diff --git a/editor/filesystem_dock.cpp b/editor/filesystem_dock.cpp index 1718badbfa..ec1153a015 100644 --- a/editor/filesystem_dock.cpp +++ b/editor/filesystem_dock.cpp @@ -139,41 +139,56 @@ void FileSystemDock::_update_tree(bool keep_collapse_state, bool p_uncollapse_ro updating_tree = false; } -void FileSystemDock::_notification(int p_what) { +void FileSystemDock::_update_display_mode() { - switch (p_what) { + bool disable_split = bool(EditorSettings::get_singleton()->get("docks/filesystem/disable_split")); + bool compact_mode = get_size().height < int(EditorSettings::get_singleton()->get("docks/filesystem/split_mode_minimum_height")); + DisplayMode new_mode; + if (disable_split || compact_mode) { + new_mode = file_list_view ? DISPLAY_FILE_LIST_ONLY : DISPLAY_TREE_ONLY; + } else { + new_mode = DISPLAY_SPLIT; + } - case NOTIFICATION_RESIZED: { + if (new_mode != display_mode) { + switch (new_mode) { + case DISPLAY_TREE_ONLY: + tree->show(); + tree->set_v_size_flags(SIZE_EXPAND_FILL); + _update_tree(true); - bool new_mode = get_size().height < get_viewport_rect().size.height / 2; + file_list_vb->hide(); + break; - if (new_mode != low_height_mode) { + case DISPLAY_FILE_LIST_ONLY: + tree->hide(); + button_tree->show(); - low_height_mode = new_mode; + file_list_vb->show(); + _update_files(true); + break; - if (low_height_mode) { + case DISPLAY_SPLIT: + tree->show(); + tree->set_v_size_flags(SIZE_EXPAND_FILL); + button_tree->hide(); + tree->ensure_cursor_is_visible(); + _update_tree(true); - tree->hide(); - tree->set_v_size_flags(SIZE_EXPAND_FILL); - button_tree->show(); - } else { + file_list_vb->show(); + _update_files(true); + break; + } + display_mode = new_mode; + } +} - tree->set_v_size_flags(SIZE_FILL); - button_tree->hide(); - if (!tree->is_visible()) { - tree->show(); - button_favorite->show(); - _update_tree(true); - } - tree->ensure_cursor_is_visible(); +void FileSystemDock::_notification(int p_what) { - if (!file_list_vb->is_visible()) { - file_list_vb->show(); - _update_files(true); - } - } - } + switch (p_what) { + case NOTIFICATION_RESIZED: { + _update_display_mode(); } break; case NOTIFICATION_ENTER_TREE: { @@ -190,13 +205,14 @@ void FileSystemDock::_notification(int p_what) { //button_instance->set_icon(get_icon("Add", ei)); //button_open->set_icon(get_icon("Folder", ei)); button_tree->set_icon(get_icon("Filesystem", ei)); - _update_file_display_toggle_button(); - button_display_mode->connect("pressed", this, "_change_file_display"); + _update_file_list_display_mode_button(); + button_file_list_display_mode->connect("pressed", this, "_change_file_display"); //file_options->set_icon( get_icon("Tools","ei")); files->connect("item_activated", this, "_select_file"); button_hist_next->connect("pressed", this, "_fw_history"); button_hist_prev->connect("pressed", this, "_bw_history"); - search_box->add_icon_override("right_icon", get_icon("Search", ei)); + search_box->set_right_icon(get_icon("Search", ei)); + search_box->set_clear_button_enabled(true); button_hist_next->set_icon(get_icon("Forward", ei)); button_hist_prev->set_icon(get_icon("Back", ei)); @@ -207,6 +223,8 @@ void FileSystemDock::_notification(int p_what) { button_tree->connect("pressed", this, "_go_to_tree", varray(), CONNECT_DEFERRED); current_path->connect("text_entered", this, "navigate_to_path"); + _update_display_mode(); + if (EditorFileSystem::get_singleton()->is_scanning()) { _set_scanning_mode(); } else { @@ -240,28 +258,29 @@ void FileSystemDock::_notification(int p_what) { } break; case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: { - + // Update icons String ei = "EditorIcons"; - int new_mode = int(EditorSettings::get_singleton()->get("docks/filesystem/display_mode")); - - //_update_icons - button_reload->set_icon(get_icon("Reload", ei)); button_favorite->set_icon(get_icon("Favorites", ei)); button_tree->set_icon(get_icon("Filesystem", ei)); button_hist_next->set_icon(get_icon("Forward", ei)); button_hist_prev->set_icon(get_icon("Back", ei)); - search_box->add_icon_override("right_icon", get_icon("Search", ei)); + search_box->set_right_icon(get_icon("Search", ei)); + search_box->set_clear_button_enabled(true); - if (new_mode != display_mode) { - set_display_mode(new_mode); + // Change size mode + int new_file_list_mode = int(EditorSettings::get_singleton()->get("docks/filesystem/display_mode")); + if (new_file_list_mode != file_list_display_mode) { + set_file_list_display_mode(new_file_list_mode); } else { - _update_file_display_toggle_button(); + _update_file_list_display_mode_button(); _update_files(true); } - _update_tree(true); + // Change full tree mode + _update_display_mode(); + } break; } } @@ -287,7 +306,7 @@ void FileSystemDock::_dir_selected() { current_path->set_text(path); _push_to_history(); - if (!low_height_mode) { + if (display_mode == DISPLAY_SPLIT) { _update_files(false); } } @@ -358,7 +377,7 @@ void FileSystemDock::navigate_to_path(const String &p_path) { current_path->set_text(path); _push_to_history(); - if (!low_height_mode) { + if (display_mode == DISPLAY_SPLIT) { _update_tree(true); _update_files(false); } else { @@ -388,24 +407,24 @@ void FileSystemDock::_thumbnail_done(const String &p_path, const Ref<Texture> &p } } -void FileSystemDock::_update_file_display_toggle_button() { +void FileSystemDock::_update_file_list_display_mode_button() { - if (button_display_mode->is_pressed()) { - display_mode = DISPLAY_LIST; - button_display_mode->set_icon(get_icon("FileThumbnail", "EditorIcons")); - button_display_mode->set_tooltip(TTR("View items as a grid of thumbnails.")); + if (button_file_list_display_mode->is_pressed()) { + file_list_display_mode = FILE_LIST_DISPLAY_LIST; + button_file_list_display_mode->set_icon(get_icon("FileThumbnail", "EditorIcons")); + button_file_list_display_mode->set_tooltip(TTR("View items as a grid of thumbnails.")); } else { - display_mode = DISPLAY_THUMBNAILS; - button_display_mode->set_icon(get_icon("FileList", "EditorIcons")); - button_display_mode->set_tooltip(TTR("View items as a list.")); + file_list_display_mode = FILE_LIST_DISPLAY_THUMBNAILS; + button_file_list_display_mode->set_icon(get_icon("FileList", "EditorIcons")); + button_file_list_display_mode->set_tooltip(TTR("View items as a list.")); } } void FileSystemDock::_change_file_display() { - _update_file_display_toggle_button(); + _update_file_list_display_mode_button(); - EditorSettings::get_singleton()->set("docks/filesystem/display_mode", display_mode); + EditorSettings::get_singleton()->set("docks/filesystem/display_mode", file_list_display_mode); _update_files(true); } @@ -470,8 +489,8 @@ void FileSystemDock::_update_files(bool p_keep_selection) { bool always_show_folders = EditorSettings::get_singleton()->get("docks/filesystem/always_show_folders"); - bool use_thumbnails = (display_mode == DISPLAY_THUMBNAILS); - bool use_folders = search_box->get_text().length() == 0 && (low_height_mode || always_show_folders); + bool use_thumbnails = (file_list_display_mode == FILE_LIST_DISPLAY_THUMBNAILS); + bool use_folders = search_box->get_text().length() == 0 && ((display_mode == DISPLAY_FILE_LIST_ONLY || display_mode == DISPLAY_TREE_ONLY) || always_show_folders); if (use_thumbnails) { @@ -615,24 +634,28 @@ void FileSystemDock::_select_file(int p_idx) { } } -void FileSystemDock::_go_to_tree() { +void FileSystemDock::_go_to_file_list() { - if (low_height_mode) { - tree->show(); - button_favorite->show(); - file_list_vb->hide(); + if (display_mode == DISPLAY_TREE_ONLY) { + file_list_view = true; + _update_display_mode(); + } else { + bool collapsed = tree->get_selected()->is_collapsed(); + tree->get_selected()->set_collapsed(!collapsed); + _update_files(false); } +} +void FileSystemDock::_go_to_tree() { - _update_tree(true); + file_list_view = false; tree->grab_focus(); + _update_display_mode(); tree->ensure_cursor_is_visible(); - //button_open->hide(); - //file_options->hide(); } void FileSystemDock::_preview_invalidated(const String &p_path) { - if (display_mode == DISPLAY_THUMBNAILS && p_path.get_base_dir() == path && search_box->get_text() == String() && file_list_vb->is_visible_in_tree()) { + if (file_list_display_mode == FILE_LIST_DISPLAY_THUMBNAILS && p_path.get_base_dir() == path && search_box->get_text() == String() && file_list_vb->is_visible_in_tree()) { for (int i = 0; i < files->get_item_count(); i++) { @@ -1408,25 +1431,7 @@ void FileSystemDock::_resource_created() const { RES current_res = RES(r); - editor->save_resource_as(current_res); -} - -void FileSystemDock::_go_to_file_list() { - - if (low_height_mode) { - tree->hide(); - file_list_vb->show(); - button_favorite->hide(); - } else { - bool collapsed = tree->get_selected()->is_collapsed(); - tree->get_selected()->set_collapsed(!collapsed); - } - - //file_options->show(); - - _update_files(false); - - //emit_signal("open",path); + editor->save_resource_as(current_res, path); } void FileSystemDock::_dir_rmb_pressed(const Vector2 &p_pos) { @@ -1448,7 +1453,7 @@ void FileSystemDock::_dir_rmb_pressed(const Vector2 &p_pos) { } folder_options->add_separator(); folder_options->add_item(TTR("New Folder..."), FOLDER_NEW_FOLDER); - folder_options->add_item(TTR("Show In File Manager"), FOLDER_SHOW_IN_EXPLORER); + folder_options->add_item(TTR("Open In File Manager"), FOLDER_SHOW_IN_EXPLORER); } folder_options->set_position(tree->get_global_position() + p_pos); folder_options->popup(); @@ -1472,7 +1477,7 @@ void FileSystemDock::fix_dependencies(const String &p_for_file) { void FileSystemDock::focus_on_filter() { - if (low_height_mode && tree->is_visible()) { + if (display_mode == DISPLAY_FILE_LIST_ONLY && tree->is_visible()) { // Tree mode, switch to files list with search box tree->hide(); file_list_vb->show(); @@ -1482,12 +1487,12 @@ void FileSystemDock::focus_on_filter() { search_box->grab_focus(); } -void FileSystemDock::set_display_mode(int p_mode) { +void FileSystemDock::set_file_list_display_mode(int p_mode) { - if (p_mode == display_mode) + if (p_mode == file_list_display_mode) return; - button_display_mode->set_pressed(p_mode == DISPLAY_LIST); + button_file_list_display_mode->set_pressed(p_mode == FILE_LIST_DISPLAY_LIST); _change_file_display(); } @@ -1757,7 +1762,10 @@ void FileSystemDock::_files_list_rmb_select(int p_item, const Vector2 &p_pos) { file_options->add_item(TTR("New Folder..."), FILE_NEW_FOLDER); file_options->add_item(TTR("New Script..."), FILE_NEW_SCRIPT); file_options->add_item(TTR("New Resource..."), FILE_NEW_RESOURCE); - file_options->add_item(TTR("Show In File Manager"), FILE_SHOW_IN_EXPLORER); + + String fpath = files->get_item_metadata(p_item); + String item_text = fpath.ends_with("/") ? TTR("Open In File Manager") : TTR("Show In File Manager"); + file_options->add_item(item_text, FILE_SHOW_IN_EXPLORER); file_options->set_position(files->get_global_position() + p_pos); file_options->popup(); @@ -2025,9 +2033,9 @@ FileSystemDock::FileSystemDock(EditorNode *p_editor) { search_box->connect("text_changed", this, "_search_changed"); path_hb->add_child(search_box); - button_display_mode = memnew(ToolButton); - button_display_mode->set_toggle_mode(true); - path_hb->add_child(button_display_mode); + button_file_list_display_mode = memnew(ToolButton); + button_file_list_display_mode->set_toggle_mode(true); + path_hb->add_child(button_file_list_display_mode); files = memnew(ItemList); files->set_v_size_flags(SIZE_EXPAND_FILL); @@ -2123,8 +2131,8 @@ FileSystemDock::FileSystemDock(EditorNode *p_editor) { history_max_size = 20; history.push_back("res://"); - low_height_mode = false; - display_mode = DISPLAY_THUMBNAILS; + display_mode = DISPLAY_SPLIT; + file_list_display_mode = FILE_LIST_DISPLAY_THUMBNAILS; } FileSystemDock::~FileSystemDock() { diff --git a/editor/filesystem_dock.h b/editor/filesystem_dock.h index 6a0c73d52e..fbbe87fc16 100644 --- a/editor/filesystem_dock.h +++ b/editor/filesystem_dock.h @@ -60,12 +60,18 @@ class FileSystemDock : public VBoxContainer { GDCLASS(FileSystemDock, VBoxContainer); public: - enum DisplayMode { - DISPLAY_THUMBNAILS, - DISPLAY_LIST + enum FileListDisplayMode { + FILE_LIST_DISPLAY_THUMBNAILS, + FILE_LIST_DISPLAY_LIST }; private: + enum DisplayMode { + DISPLAY_TREE_ONLY, + DISPLAY_FILE_LIST_ONLY, + DISPLAY_SPLIT, + }; + enum FileMenu { FILE_OPEN, FILE_INSTANCE, @@ -106,7 +112,7 @@ private: Button *button_reload; Button *button_favorite; Button *button_tree; - Button *button_display_mode; + Button *button_file_list_display_mode; Button *button_hist_next; Button *button_hist_prev; Button *button_show; @@ -115,8 +121,9 @@ private: TextureRect *search_icon; HBoxContainer *path_hb; - bool low_height_mode; + FileListDisplayMode file_list_display_mode; DisplayMode display_mode; + bool file_list_view; PopupMenu *file_options; PopupMenu *folder_options; @@ -172,7 +179,7 @@ private: void _files_gui_input(Ref<InputEvent> p_event); void _update_files(bool p_keep_selection); - void _update_file_display_toggle_button(); + void _update_file_list_display_mode_button(); void _change_file_display(); void _fs_changed(); @@ -245,6 +252,8 @@ private: void _preview_invalidated(const String &p_path); void _thumbnail_done(const String &p_path, const Ref<Texture> &p_preview, const Variant &p_udata); + void _update_display_mode(); + protected: void _notification(int p_what); static void _bind_methods(); @@ -258,7 +267,7 @@ public: void fix_dependencies(const String &p_for_file); - void set_display_mode(int p_mode); + void set_file_list_display_mode(int p_mode); int get_split_offset() { return split_box->get_split_offset(); } void set_split_offset(int p_offset) { split_box->set_split_offset(p_offset); } diff --git a/editor/find_in_files.cpp b/editor/find_in_files.cpp index 2be1f1644e..ef7409fd43 100644 --- a/editor/find_in_files.cpp +++ b/editor/find_in_files.cpp @@ -37,10 +37,10 @@ #include "scene/gui/check_box.h" #include "scene/gui/file_dialog.h" #include "scene/gui/grid_container.h" -#include "scene/gui/item_list.h" #include "scene/gui/label.h" #include "scene/gui/line_edit.h" #include "scene/gui/progress_bar.h" +#include "scene/gui/tree.h" #define ROOT_PREFIX "res://" @@ -58,6 +58,34 @@ static bool is_text_char(CharType c) { return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_'; } +static bool find_next(const String &line, String pattern, int from, bool match_case, bool whole_words, int &out_begin, int &out_end) { + + int end = from; + + while (true) { + int begin = match_case ? line.find(pattern, end) : line.findn(pattern, end); + + if (begin == -1) + return false; + + end = begin + pattern.length(); + out_begin = begin; + out_end = end; + + if (whole_words) { + if (begin > 0 && is_text_char(line[begin - 1])) { + continue; + } + if (end < line.size() && is_text_char(line[end])) { + continue; + } + } + + return true; + } +} + +//-------------------------------------------------------------------------------- FindInFiles::FindInFiles() { _root_prefix = ROOT_PREFIX; _extension_filter.insert("gd"); @@ -246,24 +274,7 @@ void FindInFiles::_scan_file(String fpath) { String line = f->get_line(); - // Find all occurrences in the current line - while (true) { - begin = _match_case ? line.find(_pattern, end) : line.findn(_pattern, end); - - if (begin == -1) - break; - - end = begin + _pattern.length(); - - if (_whole_words) { - if (begin > 0 && is_text_char(line[begin - 1])) { - continue; - } - if (end < line.size() && is_text_char(line[end])) { - continue; - } - } - + while (find_next(line, _pattern, end, _match_case, _whole_words, begin, end)) { emit_signal(SIGNAL_RESULT_FOUND, fpath, line_number, begin, end, line); } } @@ -567,14 +578,18 @@ FindInFilesPanel::FindInFilesPanel() { vbc->add_child(hbc); } - // In the future, this should be replaced by a more specific list container, - // which can highlight text regions and change opacity for enabled/disabled states - _results_display = memnew(ItemList); + _results_display = memnew(Tree); _results_display->add_font_override("font", get_font("source", "EditorFonts")); _results_display->set_v_size_flags(SIZE_EXPAND_FILL); _results_display->connect("item_selected", this, "_on_result_selected"); + _results_display->connect("item_edited", this, "_on_item_edited"); + _results_display->set_hide_root(true); + _results_display->set_select_mode(Tree::SELECT_ROW); + _results_display->create_item(); // Root vbc->add_child(_results_display); + _with_replace = false; + { _replace_container = memnew(HBoxContainer); @@ -600,12 +615,33 @@ FindInFilesPanel::FindInFilesPanel() { void FindInFilesPanel::set_with_replace(bool with_replace) { + _with_replace = with_replace; _replace_container->set_visible(with_replace); + + if (with_replace) { + // Results show checkboxes on their left so they can be opted out + _results_display->set_columns(2); + _results_display->set_column_expand(0, false); + _results_display->set_column_min_width(0, 48 * EDSCALE); + + } else { + // Results are single-cell items + _results_display->set_column_expand(0, true); + _results_display->set_columns(1); + } +} + +void FindInFilesPanel::clear() { + _file_items.clear(); + _result_items.clear(); + _results_display->clear(); + _results_display->create_item(); // Root } void FindInFilesPanel::start_search() { - _results_display->clear(); + clear(); + _status_label->set_text(TTR("Searching...")); _search_text_label->set_text(_finder->get_search_text()); @@ -636,9 +672,90 @@ void FindInFilesPanel::_notification(int p_what) { void FindInFilesPanel::_on_result_found(String fpath, int line_number, int begin, int end, String text) { - int i = _results_display->get_item_count(); - _results_display->add_item(fpath + ": " + String::num(line_number) + ": " + text.replace("\t", " ")); - _results_display->set_item_metadata(i, varray(fpath, line_number, begin, end)); + TreeItem *file_item; + Map<String, TreeItem *>::Element *E = _file_items.find(fpath); + + if (E == NULL) { + file_item = _results_display->create_item(); + file_item->set_text(0, fpath); + file_item->set_metadata(0, fpath); + + // The width of this column is restrained to checkboxes, but that doesn't make sense for the parent items, + // so we override their width so they can expand to full width + file_item->set_expand_right(0, true); + + _file_items[fpath] = file_item; + + } else { + file_item = E->value(); + } + + int text_index = _with_replace ? 1 : 0; + + TreeItem *item = _results_display->create_item(file_item); + + // Do this first because it resets properties of the cell... + item->set_cell_mode(text_index, TreeItem::CELL_MODE_CUSTOM); + + String item_text = String::num_int64(line_number) + ": " + text.replace("\t", " "); + + item->set_text(text_index, item_text); + item->set_custom_draw(text_index, this, "_draw_result_text"); + + Ref<Font> font = _results_display->get_font("font"); + + float raw_text_width = font->get_string_size(text).x; + float item_text_width = font->get_string_size(item_text).x; + + Result r; + r.line_number = line_number; + r.begin = begin; + r.end = end; + r.draw_begin = (item_text_width - raw_text_width) + font->get_string_size(text.left(r.begin)).x; + r.draw_width = font->get_string_size(text.substr(r.begin, r.end - r.begin + 1)).x; + _result_items[item] = r; + + if (_with_replace) { + item->set_cell_mode(0, TreeItem::CELL_MODE_CHECK); + item->set_checked(0, true); + item->set_editable(0, true); + } +} + +void FindInFilesPanel::draw_result_text(Object *item_obj, Rect2 rect) { + + TreeItem *item = Object::cast_to<TreeItem>(item_obj); + if (!item) + return; + + Map<TreeItem *, Result>::Element *E = _result_items.find(item); + if (!E) + return; + Result r = E->value(); + + Rect2 match_rect = rect; + match_rect.position.x += r.draw_begin; + match_rect.size.x = r.draw_width; + match_rect.position.y += 1 * EDSCALE; + match_rect.size.y -= 2 * EDSCALE; + + _results_display->draw_rect(match_rect, Color(0, 0, 0, 0.5)); + // Text is drawn by Tree already +} + +void FindInFilesPanel::_on_item_edited() { + + TreeItem *item = _results_display->get_selected(); + + if (item->is_checked(0)) { + item->set_custom_color(1, _results_display->get_color("font_color")); + + } else { + // Grey out + Color color = _results_display->get_color("font_color"); + color.a /= 2.0; + item->set_custom_color(1, color); + } } void FindInFilesPanel::_on_finished() { @@ -653,10 +770,19 @@ void FindInFilesPanel::_on_cancel_button_clicked() { stop_search(); } -void FindInFilesPanel::_on_result_selected(int i) { +void FindInFilesPanel::_on_result_selected() { + + TreeItem *item = _results_display->get_selected(); + Map<TreeItem *, Result>::Element *E = _result_items.find(item); + + if (E == NULL) + return; + Result r = E->value(); + + TreeItem *file_item = item->get_parent(); + String fpath = file_item->get_metadata(0); - Array meta = _results_display->get_item_metadata(i); - emit_signal(SIGNAL_RESULT_SELECTED, meta[0], meta[1], meta[2], meta[3]); + emit_signal(SIGNAL_RESULT_SELECTED, fpath, r.line_number, r.begin, r.end); } void FindInFilesPanel::_on_replace_text_changed(String text) { @@ -668,39 +794,33 @@ void FindInFilesPanel::_on_replace_all_clicked() { String replace_text = get_replace_text(); ERR_FAIL_COND(replace_text.empty()); - String last_fpath; - PoolIntArray locations; PoolStringArray modified_files; - for (int i = 0; i < _results_display->get_item_count(); ++i) { + for (Map<String, TreeItem *>::Element *E = _file_items.front(); E; E = E->next()) { - Array meta = _results_display->get_item_metadata(i); + TreeItem *file_item = E->value(); + String fpath = file_item->get_metadata(0); - String fpath = meta[0]; + Vector<Result> locations; + for (TreeItem *item = file_item->get_children(); item; item = item->get_next()) { - // Results are sorted by file, so we can batch replaces - if (fpath != last_fpath) { - if (locations.size() != 0) { - apply_replaces_in_file(last_fpath, locations, replace_text); - modified_files.append(last_fpath); - locations.resize(0); - } - } + if (!item->is_checked(0)) + continue; - locations.append(meta[1]); // line_number - locations.append(meta[2]); // begin - locations.append(meta[3]); // end - - last_fpath = fpath; - } + Map<TreeItem *, Result>::Element *E = _result_items.find(item); + ERR_FAIL_COND(E == NULL); + locations.push_back(E->value()); + } - if (locations.size() != 0) { - apply_replaces_in_file(last_fpath, locations, replace_text); - modified_files.append(last_fpath); + if (locations.size() != 0) { + // Results are sorted by file, so we can batch replaces + apply_replaces_in_file(fpath, locations, replace_text); + modified_files.append(fpath); + } } // Hide replace bar so we can't trigger the action twice without doing a new search - set_with_replace(false); + _replace_container->hide(); emit_signal(SIGNAL_FILES_MODIFIED, modified_files); } @@ -740,11 +860,7 @@ private: Vector<char> _line_buffer; }; -void FindInFilesPanel::apply_replaces_in_file(String fpath, PoolIntArray locations, String text) { - - ERR_FAIL_COND(locations.size() % 3 != 0); - - //print_line(String("Replacing {0} occurrences in {1}").format(varray(fpath, locations.size() / 3))); +void FindInFilesPanel::apply_replaces_in_file(String fpath, const Vector<Result> &locations, String new_text) { // If the file is already open, I assume the editor will reload it. // If there are unsaved changes, the user will be asked on focus, @@ -759,21 +875,34 @@ void FindInFilesPanel::apply_replaces_in_file(String fpath, PoolIntArray locatio ConservativeGetLine conservative; String line = conservative.get_line(f); + String search_text = _finder->get_search_text(); + + int offset = 0; - PoolIntArray::Read locations_read = locations.read(); - for (int i = 0; i < locations.size(); i += 3) { + for (int i = 0; i < locations.size(); ++i) { - int repl_line_number = locations_read[i]; - int repl_begin = locations_read[i + 1]; - int repl_end = locations_read[i + 2]; + int repl_line_number = locations[i].line_number; while (current_line < repl_line_number) { buffer += line; line = conservative.get_line(f); ++current_line; + offset = 0; + } + + int repl_begin = locations[i].begin + offset; + int repl_end = locations[i].end + offset; + + int _; + if (!find_next(line, search_text, repl_begin, _finder->is_match_case(), _finder->is_whole_words(), _, _)) { + // Make sure the replace is still valid in case the file was tampered with. + print_line(String("Occurrence no longer matches, replace will be ignored in {0}: line {1}, col {2}").format(varray(fpath, repl_line_number, repl_begin))); + continue; } - line = line.left(repl_begin) + text + line.right(repl_end); + line = line.left(repl_begin) + new_text + line.right(repl_end); + // keep an offset in case there are successive replaces in the same line + offset += new_text.length() - (repl_end - repl_begin); } buffer += line; @@ -811,11 +940,13 @@ void FindInFilesPanel::set_progress_visible(bool visible) { void FindInFilesPanel::_bind_methods() { ClassDB::bind_method("_on_result_found", &FindInFilesPanel::_on_result_found); + ClassDB::bind_method("_on_item_edited", &FindInFilesPanel::_on_item_edited); ClassDB::bind_method("_on_finished", &FindInFilesPanel::_on_finished); ClassDB::bind_method("_on_cancel_button_clicked", &FindInFilesPanel::_on_cancel_button_clicked); ClassDB::bind_method("_on_result_selected", &FindInFilesPanel::_on_result_selected); ClassDB::bind_method("_on_replace_text_changed", &FindInFilesPanel::_on_replace_text_changed); ClassDB::bind_method("_on_replace_all_clicked", &FindInFilesPanel::_on_replace_all_clicked); + ClassDB::bind_method("_draw_result_text", &FindInFilesPanel::draw_result_text); ADD_SIGNAL(MethodInfo(SIGNAL_RESULT_SELECTED, PropertyInfo(Variant::STRING, "path"), diff --git a/editor/find_in_files.h b/editor/find_in_files.h index d57184960b..75ea1c3161 100644 --- a/editor/find_in_files.h +++ b/editor/find_in_files.h @@ -131,7 +131,8 @@ private: }; class Button; -class ItemList; +class Tree; +class TreeItem; class ProgressBar; // Display search results @@ -159,22 +160,37 @@ private: void _on_result_found(String fpath, int line_number, int begin, int end, String text); void _on_finished(); void _on_cancel_button_clicked(); - void _on_result_selected(int i); + void _on_result_selected(); + void _on_item_edited(); void _on_replace_text_changed(String text); void _on_replace_all_clicked(); - void apply_replaces_in_file(String fpath, PoolIntArray locations, String text); + struct Result { + int line_number; + int begin; + int end; + float draw_begin; + float draw_width; + }; + void apply_replaces_in_file(String fpath, const Vector<Result> &locations, String new_text); void update_replace_buttons(); String get_replace_text(); + + void draw_result_text(Object *item_obj, Rect2 rect); + void set_progress_visible(bool visible); + void clear(); FindInFiles *_finder; Label *_search_text_label; - ItemList *_results_display; + Tree *_results_display; Label *_status_label; Button *_cancel_button; ProgressBar *_progress_bar; + Map<String, TreeItem *> _file_items; + Map<TreeItem *, Result> _result_items; + bool _with_replace; HBoxContainer *_replace_container; LineEdit *_replace_line_edit; diff --git a/editor/groups_editor.cpp b/editor/groups_editor.cpp index 2bfd2eb5c3..0efd14e932 100644 --- a/editor/groups_editor.cpp +++ b/editor/groups_editor.cpp @@ -287,8 +287,10 @@ void GroupDialog::_notification(int p_what) { add_button->set_icon(get_icon("Forward", "EditorIcons")); remove_button->set_icon(get_icon("Back", "EditorIcons")); - add_filter->add_icon_override("right_icon", get_icon("Search", "EditorIcons")); - remove_filter->add_icon_override("right_icon", get_icon("Search", "EditorIcons")); + add_filter->set_right_icon(get_icon("Search", "EditorIcons")); + add_filter->set_clear_button_enabled(true); + remove_filter->set_right_icon(get_icon("Search", "EditorIcons")); + remove_filter->set_clear_button_enabled(true); } break; } } diff --git a/editor/import/resource_importer_texture.cpp b/editor/import/resource_importer_texture.cpp index 17a9394b51..a2d54e0048 100644 --- a/editor/import/resource_importer_texture.cpp +++ b/editor/import/resource_importer_texture.cpp @@ -198,6 +198,7 @@ void ResourceImporterTexture::get_import_options(List<ImportOption> *r_options, r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "process/fix_alpha_border"), p_preset != PRESET_3D ? true : false)); r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "process/premult_alpha"), false)); r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "process/HDR_as_SRGB"), false)); + r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "process/invert_color"), false)); r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "stream"), false)); r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "size_limit", PROPERTY_HINT_RANGE, "0,4096,1"), 0)); r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "detect_3d"), p_preset == PRESET_DETECT)); @@ -354,6 +355,7 @@ Error ResourceImporterTexture::import(const String &p_source_file, const String int srgb = p_options["flags/srgb"]; bool fix_alpha_border = p_options["process/fix_alpha_border"]; bool premult_alpha = p_options["process/premult_alpha"]; + bool invert_color = p_options["process/invert_color"]; bool stream = p_options["stream"]; int size_limit = p_options["size_limit"]; bool force_rgbe = int(p_options["compress/hdr_mode"]) == 1; @@ -409,6 +411,19 @@ Error ResourceImporterTexture::import(const String &p_source_file, const String image->premultiply_alpha(); } + if (invert_color) { + int height = image->get_height(); + int width = image->get_width(); + + image->lock(); + for (int i = 0; i < height; i++) { + for (int j = 0; j < width; j++) { + image->set_pixel(i, j, image->get_pixel(i, j).inverted()); + } + } + image->unlock(); + } + bool detect_3d = p_options["detect_3d"]; bool detect_srgb = srgb == 2; bool detect_normal = normal == 0; diff --git a/editor/import/resource_importer_wav.cpp b/editor/import/resource_importer_wav.cpp index 41f5a892eb..9e99dcc5c8 100644 --- a/editor/import/resource_importer_wav.cpp +++ b/editor/import/resource_importer_wav.cpp @@ -157,15 +157,18 @@ Error ResourceImporterWAV::import(const String &p_source_file, const String &p_s //Consider revision for engine version 3.0 compression_code = file->get_16(); if (compression_code != 1 && compression_code != 3) { - ERR_PRINT("Format not supported for WAVE file (not PCM). Save WAVE files as uncompressed PCM instead."); - break; + file->close(); + memdelete(file); + ERR_EXPLAIN("Format not supported for WAVE file (not PCM). Save WAVE files as uncompressed PCM instead."); + ERR_FAIL_V(ERR_INVALID_DATA); } format_channels = file->get_16(); if (format_channels != 1 && format_channels != 2) { - - ERR_PRINT("Format not supported for WAVE file (not stereo or mono)"); - break; + file->close(); + memdelete(file); + ERR_EXPLAIN("Format not supported for WAVE file (not stereo or mono)."); + ERR_FAIL_V(ERR_INVALID_DATA); } format_freq = file->get_32(); //sampling rate @@ -174,10 +177,11 @@ Error ResourceImporterWAV::import(const String &p_source_file, const String &p_s file->get_16(); // block align (unused) format_bits = file->get_16(); // bits per sample - if (format_bits % 8) { - - ERR_PRINT("Strange number of bits in sample (not 8,16,24,32)"); - break; + if (format_bits % 8 || format_bits == 0) { + file->close(); + memdelete(file); + ERR_EXPLAIN("Invalid amount of bits in the sample (should be one of 8, 16, 24 or 32)."); + ERR_FAIL_V(ERR_INVALID_DATA); } /* Don't need anything else, continue */ @@ -185,7 +189,7 @@ Error ResourceImporterWAV::import(const String &p_source_file, const String &p_s } if (chunkID[0] == 'd' && chunkID[1] == 'a' && chunkID[2] == 't' && chunkID[3] == 'a' && !data_found) { - /* IS FORMAT CHUNK */ + /* IS DATA CHUNK */ data_found = true; if (!format_found) { diff --git a/editor/import_dock.cpp b/editor/import_dock.cpp index f91802b352..31eb193461 100644 --- a/editor/import_dock.cpp +++ b/editor/import_dock.cpp @@ -420,10 +420,9 @@ ImportDock::ImportDock() { preset->get_popup()->connect("index_pressed", this, "_preset_selected"); hb->add_child(preset); - import_opts = memnew(PropertyEditor); + import_opts = memnew(EditorInspector); add_child(import_opts); import_opts->set_v_size_flags(SIZE_EXPAND_FILL); - import_opts->hide_top_label(); hb = memnew(HBoxContainer); add_child(hb); diff --git a/editor/import_dock.h b/editor/import_dock.h index a7a7eda8d8..41c7298d9a 100644 --- a/editor/import_dock.h +++ b/editor/import_dock.h @@ -31,10 +31,12 @@ #ifndef IMPORTDOCK_H #define IMPORTDOCK_H -#include "editor_file_system.h" -#include "io/resource_import.h" -#include "property_editor.h" +#include "core/io/config_file.h" +#include "core/io/resource_import.h" +#include "editor/editor_file_system.h" +#include "editor/editor_inspector.h" #include "scene/gui/box_container.h" +#include "scene/gui/menu_button.h" #include "scene/gui/option_button.h" #include "scene/gui/popup_menu.h" @@ -45,7 +47,7 @@ class ImportDock : public VBoxContainer { Label *imported; OptionButton *import_as; MenuButton *preset; - PropertyEditor *import_opts; + EditorInspector *import_opts; List<PropertyInfo> properties; Map<StringName, Variant> property_values; diff --git a/editor/inspector_dock.cpp b/editor/inspector_dock.cpp index e65b743bfa..0335053162 100644 --- a/editor/inspector_dock.cpp +++ b/editor/inspector_dock.cpp @@ -36,6 +36,16 @@ void InspectorDock::_menu_option(int p_option) { switch (p_option) { + case RESOURCE_MAKE_BUILT_IN: { + _unref_resource(); + } break; + case RESOURCE_COPY: { + _copy_resource(); + } break; + case RESOURCE_EDIT_CLIPBOARD: { + _paste_resource(); + } break; + case RESOURCE_SAVE: { _save_resource(false); } break; @@ -400,10 +410,11 @@ void InspectorDock::update(Object *p_object) { p->add_shortcut(ED_SHORTCUT("property_editor/copy_params", TTR("Copy Params")), OBJECT_COPY_PARAMS); p->add_shortcut(ED_SHORTCUT("property_editor/paste_params", TTR("Paste Params")), OBJECT_PASTE_PARAMS); p->add_separator(); - p->add_shortcut(ED_SHORTCUT("property_editor/paste_resource", TTR("Paste Resource")), RESOURCE_PASTE); + + p->add_shortcut(ED_SHORTCUT("property_editor/paste_resource", TTR("Edit Resource Clipboard")), RESOURCE_EDIT_CLIPBOARD); if (is_resource) { p->add_shortcut(ED_SHORTCUT("property_editor/copy_resource", TTR("Copy Resource")), RESOURCE_COPY); - p->add_shortcut(ED_SHORTCUT("property_editor/unref_resource", TTR("Make Built-In")), RESOURCE_UNREF); + p->add_shortcut(ED_SHORTCUT("property_editor/unref_resource", TTR("Make Built-In")), RESOURCE_MAKE_BUILT_IN); } if (is_resource || is_node) { @@ -529,7 +540,8 @@ InspectorDock::InspectorDock(EditorNode *p_editor, EditorData &p_editor_data) { search = memnew(LineEdit); search->set_h_size_flags(Control::SIZE_EXPAND_FILL); search->set_placeholder(TTR("Filter properties")); - search->add_icon_override("right_icon", get_icon("Search", "EditorIcons")); + search->set_right_icon(get_icon("Search", "EditorIcons")); + search->set_clear_button_enabled(true); add_child(search); warning = memnew(Button); diff --git a/editor/inspector_dock.h b/editor/inspector_dock.h index f347056158..97ef6899dc 100644 --- a/editor/inspector_dock.h +++ b/editor/inspector_dock.h @@ -51,13 +51,12 @@ class InspectorDock : public VBoxContainer { GDCLASS(InspectorDock, VBoxContainer); enum MenuOptions { - RESOURCE_NEW, RESOURCE_LOAD, RESOURCE_SAVE, RESOURCE_SAVE_AS, - RESOURCE_UNREF, + RESOURCE_MAKE_BUILT_IN, RESOURCE_COPY, - RESOURCE_PASTE, + RESOURCE_EDIT_CLIPBOARD, OBJECT_COPY_PARAMS, OBJECT_PASTE_PARAMS, OBJECT_UNIQUE_RESOURCES, diff --git a/editor/plugin_config_dialog.cpp b/editor/plugin_config_dialog.cpp new file mode 100644 index 0000000000..418936ac9f --- /dev/null +++ b/editor/plugin_config_dialog.cpp @@ -0,0 +1,230 @@ +/*************************************************************************/ +/* plugin_config_dialog.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "plugin_config_dialog.h" +#include "core/io/config_file.h" +#include "core/os/dir_access.h" +#include "editor/editor_node.h" +#include "editor/editor_plugin.h" +#include "modules/gdscript/gdscript.h" +#include "scene/gui/grid_container.h" + +void PluginConfigDialog::_clear_fields() { + name_edit->set_text(""); + subfolder_edit->set_text(""); + desc_edit->set_text(""); + author_edit->set_text(""); + version_edit->set_text(""); + script_edit->set_text(""); +} + +void PluginConfigDialog::_on_confirmed() { + + String path = "res://addons/" + subfolder_edit->get_text(); + + if (!_edit_mode) { + DirAccess *d = DirAccess::create(DirAccess::ACCESS_RESOURCES); + if (!d || d->make_dir_recursive(path) != OK) + return; + } + + Ref<ConfigFile> cf = memnew(ConfigFile); + cf->set_value("plugin", "name", name_edit->get_text()); + cf->set_value("plugin", "description", desc_edit->get_text()); + cf->set_value("plugin", "author", author_edit->get_text()); + cf->set_value("plugin", "version", version_edit->get_text()); + cf->set_value("plugin", "script", script_edit->get_text()); + + cf->save(path.plus_file("plugin.cfg")); + + if (!_edit_mode) { + String type = script_option_edit->get_item_text(script_option_edit->get_selected()); + + Ref<Script> script; + + if (type == GDScriptLanguage::get_singleton()->get_name()) { + Ref<GDScript> gdscript = memnew(GDScript); + gdscript->set_source_code( + "tool\n" + "extends EditorPlugin\n" + "\n" + "func _enter_tree():\n" + "\tpass"); + String script_path = path.plus_file(script_edit->get_text()); + gdscript->set_path(script_path); + ResourceSaver::save(script_path, gdscript); + script = gdscript; + } + //TODO: other languages + + emit_signal("plugin_ready", script.operator->(), active_edit->is_pressed() ? name_edit->get_text() : ""); + } else { + EditorNode::get_singleton()->get_project_settings()->update_plugins(); + } + _clear_fields(); +} + +void PluginConfigDialog::_on_cancelled() { + _clear_fields(); +} + +void PluginConfigDialog::_on_required_text_changed(const String &p_text) { + String ext = script_option_edit->get_item_metadata(script_option_edit->get_selected()); + get_ok()->set_disabled(script_edit->get_text().get_basename().empty() || script_edit->get_text().get_extension() != ext || name_edit->get_text().empty()); +} + +void PluginConfigDialog::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_READY: { + connect("confirmed", this, "_on_confirmed"); + get_cancel()->connect("pressed", this, "_on_cancelled"); + } break; + } +} + +void PluginConfigDialog::config(const String &p_config_path) { + if (p_config_path.length()) { + Ref<ConfigFile> cf = memnew(ConfigFile); + print_line(p_config_path); + cf->load(p_config_path); + + name_edit->set_text(cf->get_value("plugin", "name", "")); + subfolder_edit->set_text(p_config_path.get_base_dir().get_basename().get_file()); + desc_edit->set_text(cf->get_value("plugin", "description", "")); + author_edit->set_text(cf->get_value("plugin", "author", "")); + version_edit->set_text(cf->get_value("plugin", "version", "")); + script_edit->set_text(cf->get_value("plugin", "script", "")); + + _edit_mode = true; + active_edit->hide(); + Object::cast_to<Label>(active_edit->get_parent()->get_child(active_edit->get_index() - 1))->hide(); + subfolder_edit->hide(); + Object::cast_to<Label>(subfolder_edit->get_parent()->get_child(subfolder_edit->get_index() - 1))->hide(); + set_title(TTR("Edit a Plugin")); + } else { + _clear_fields(); + _edit_mode = false; + active_edit->show(); + Object::cast_to<Label>(active_edit->get_parent()->get_child(active_edit->get_index() - 1))->show(); + subfolder_edit->show(); + Object::cast_to<Label>(subfolder_edit->get_parent()->get_child(subfolder_edit->get_index() - 1))->show(); + set_title(TTR("Create a Plugin")); + } + get_ok()->set_disabled(!_edit_mode); + get_ok()->set_text(_edit_mode ? TTR("Update") : TTR("Create")); +} + +void PluginConfigDialog::_bind_methods() { + ClassDB::bind_method("_on_required_text_changed", &PluginConfigDialog::_on_required_text_changed); + ClassDB::bind_method("_on_confirmed", &PluginConfigDialog::_on_confirmed); + ClassDB::bind_method("_on_cancelled", &PluginConfigDialog::_on_cancelled); + ADD_SIGNAL(MethodInfo("plugin_ready", PropertyInfo(Variant::STRING, "script_path", PROPERTY_HINT_NONE, ""), PropertyInfo(Variant::STRING, "activate_name"))); +} + +PluginConfigDialog::PluginConfigDialog() { + get_ok()->set_disabled(true); + set_hide_on_ok(true); + + GridContainer *grid = memnew(GridContainer); + grid->set_columns(2); + add_child(grid); + + Label *name_lb = memnew(Label); + name_lb->set_text(TTR("Plugin Name:")); + grid->add_child(name_lb); + + name_edit = memnew(LineEdit); + name_edit->connect("text_changed", this, "_on_required_text_changed"); + name_edit->set_placeholder("MyPlugin"); + grid->add_child(name_edit); + + Label *subfolder_lb = memnew(Label); + subfolder_lb->set_text(TTR("Subfolder:")); + grid->add_child(subfolder_lb); + + subfolder_edit = memnew(LineEdit); + subfolder_edit->set_placeholder("\"my_plugin\" -> res://addons/my_plugin"); + grid->add_child(subfolder_edit); + + Label *desc_lb = memnew(Label); + desc_lb->set_text(TTR("Description:")); + grid->add_child(desc_lb); + + desc_edit = memnew(TextEdit); + desc_edit->set_custom_minimum_size(Size2(400.0f, 50.0f)); + grid->add_child(desc_edit); + + Label *author_lb = memnew(Label); + author_lb->set_text(TTR("Author:")); + grid->add_child(author_lb); + + author_edit = memnew(LineEdit); + author_edit->set_placeholder("Godette"); + grid->add_child(author_edit); + + Label *version_lb = memnew(Label); + version_lb->set_text(TTR("Version:")); + grid->add_child(version_lb); + + version_edit = memnew(LineEdit); + version_edit->set_placeholder("1.0"); + grid->add_child(version_edit); + + Label *script_option_lb = memnew(Label); + script_option_lb->set_text(TTR("Language:")); + grid->add_child(script_option_lb); + + script_option_edit = memnew(OptionButton); + script_option_edit->add_item(GDScriptLanguage::get_singleton()->get_name()); + script_option_edit->set_item_metadata(0, GDScriptLanguage::get_singleton()->get_extension()); + script_option_edit->select(0); + //TODO: add other languages + grid->add_child(script_option_edit); + + Label *script_lb = memnew(Label); + script_lb->set_text(TTR("Script Name:")); + grid->add_child(script_lb); + + script_edit = memnew(LineEdit); + script_edit->connect("text_changed", this, "_on_required_text_changed"); + script_edit->set_placeholder("\"plugin.gd\" -> res://addons/my_plugin/plugin.gd"); + grid->add_child(script_edit); + + Label *active_lb = memnew(Label); + active_lb->set_text(TTR("Activate now?")); + grid->add_child(active_lb); + + active_edit = memnew(CheckBox); + active_edit->set_pressed(true); + grid->add_child(active_edit); +} + +PluginConfigDialog::~PluginConfigDialog() { +} diff --git a/editor/plugin_config_dialog.h b/editor/plugin_config_dialog.h new file mode 100644 index 0000000000..2d321a479d --- /dev/null +++ b/editor/plugin_config_dialog.h @@ -0,0 +1,71 @@ +/*************************************************************************/ +/* plugin_config_dialog.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef PLUGIN_CONFIG_DIALOG_H +#define PLUGIN_CONFIG_DIALOG_H + +#include "scene/gui/check_box.h" +#include "scene/gui/dialogs.h" +#include "scene/gui/line_edit.h" +#include "scene/gui/option_button.h" +#include "scene/gui/text_edit.h" + +class PluginConfigDialog : public ConfirmationDialog { + + GDCLASS(PluginConfigDialog, ConfirmationDialog); + + LineEdit *name_edit; + LineEdit *subfolder_edit; + TextEdit *desc_edit; + LineEdit *author_edit; + LineEdit *version_edit; + OptionButton *script_option_edit; + LineEdit *script_edit; + CheckBox *active_edit; + + bool _edit_mode; + + void _clear_fields(); + void _on_confirmed(); + void _on_cancelled(); + void _on_required_text_changed(const String &p_text); + +protected: + virtual void _notification(int p_what); + static void _bind_methods(); + +public: + void config(const String &p_plugin_dir_name); + + PluginConfigDialog(); + ~PluginConfigDialog(); +}; + +#endif // PLUGIN_CONFIG_DIALOG_H diff --git a/editor/plugins/animation_blend_space_1d_editor.cpp b/editor/plugins/animation_blend_space_1d_editor.cpp index 2e128db883..1106464edf 100644 --- a/editor/plugins/animation_blend_space_1d_editor.cpp +++ b/editor/plugins/animation_blend_space_1d_editor.cpp @@ -3,41 +3,11 @@ #include "os/keyboard.h" #include "scene/animation/animation_blend_tree.h" -void AnimationNodeBlendSpace1DEditorPlugin::edit(Object *p_object) { - anim_tree_editor->edit(Object::cast_to<AnimationNodeBlendSpace1D>(p_object)); +StringName AnimationNodeBlendSpace1DEditor::get_blend_position_path() const { + StringName path = AnimationTreeEditor::get_singleton()->get_base_path()+"blend_position"; + return path; } -bool AnimationNodeBlendSpace1DEditorPlugin::handles(Object *p_object) const { - return p_object->is_class("AnimationNodeBlendSpace1D"); -} - -void AnimationNodeBlendSpace1DEditorPlugin::make_visible(bool p_visible) { - - if (p_visible) { - button->show(); - editor->make_bottom_panel_item_visible(anim_tree_editor); - anim_tree_editor->set_process(true); - } else { - if (anim_tree_editor->is_visible_in_tree()) { - editor->hide_bottom_panel(); - } - - button->hide(); - anim_tree_editor->set_process(false); - } -} - -AnimationNodeBlendSpace1DEditorPlugin::AnimationNodeBlendSpace1DEditorPlugin(EditorNode *p_node) { - editor = p_node; - anim_tree_editor = memnew(AnimationNodeBlendSpace1DEditor); - anim_tree_editor->set_custom_minimum_size(Size2(0, 150 * EDSCALE)); - - button = editor->add_bottom_panel_item(TTR("BlendSpace1D"), anim_tree_editor); - button->hide(); -} - -AnimationNodeBlendSpace1DEditorPlugin::~AnimationNodeBlendSpace1DEditorPlugin() { -} void AnimationNodeBlendSpace1DEditor::_blend_space_gui_input(const Ref<InputEvent> &p_event) { Ref<InputEventKey> k = p_event; @@ -62,7 +32,7 @@ void AnimationNodeBlendSpace1DEditor::_blend_space_gui_input(const Ref<InputEven menu->add_submenu_item(TTR("Add Animation"), "animations"); - AnimationTree *gp = blend_space->get_tree(); + AnimationTree *gp = AnimationTreeEditor::get_singleton()->get_tree(); ERR_FAIL_COND(!gp); if (gp->has_node(gp->get_animation_player())) { @@ -85,10 +55,18 @@ void AnimationNodeBlendSpace1DEditor::_blend_space_gui_input(const Ref<InputEven continue; int idx = menu->get_item_count(); - menu->add_item(vformat("Add %s", name)); + menu->add_item(vformat("Add %s", name),idx); menu->set_item_metadata(idx, E->get()); } + Ref<AnimationNode> clipb = EditorSettings::get_singleton()->get_resource_clipboard(); + if (clipb.is_valid()) { + menu->add_separator(); + menu->add_item(TTR("Paste"), MENU_PASTE); + } + menu->add_separator(); + menu->add_item(TTR("Load.."), MENU_LOAD_FILE); + menu->set_global_position(blend_space_draw->get_global_transform().xform(mb->get_position())); menu->popup(); @@ -158,7 +136,7 @@ void AnimationNodeBlendSpace1DEditor::_blend_space_gui_input(const Ref<InputEven blend_pos *= blend_space->get_max_space() - blend_space->get_min_space(); blend_pos += blend_space->get_min_space(); - blend_space->set_blend_pos(blend_pos); + AnimationTreeEditor::get_singleton()->get_tree()->set(get_blend_position_path(),blend_pos); blend_space_draw->update(); } @@ -181,7 +159,8 @@ void AnimationNodeBlendSpace1DEditor::_blend_space_gui_input(const Ref<InputEven blend_pos *= blend_space->get_max_space() - blend_space->get_min_space(); blend_pos += blend_space->get_min_space(); - blend_space->set_blend_pos(blend_pos); + AnimationTreeEditor::get_singleton()->get_tree()->set(get_blend_position_path(),blend_pos); + blend_space_draw->update(); } } @@ -277,7 +256,9 @@ void AnimationNodeBlendSpace1DEditor::_blend_space_draw() { color.a *= 0.5; } - float point = blend_space->get_blend_pos(); + float point = AnimationTreeEditor::get_singleton()->get_tree()->get(get_blend_position_path()); + + point = (point - blend_space->get_min_space()) / (blend_space->get_max_space() - blend_space->get_min_space()); point *= s.width; @@ -299,12 +280,6 @@ void AnimationNodeBlendSpace1DEditor::_update_space() { updating = true; - if (blend_space->get_parent().is_valid()) { - goto_parent_hb->show(); - } else { - goto_parent_hb->hide(); - } - max_value->set_value(blend_space->get_max_space()); min_value->set_value(blend_space->get_min_space()); @@ -355,15 +330,47 @@ void AnimationNodeBlendSpace1DEditor::_snap_toggled() { blend_space_draw->update(); } +void AnimationNodeBlendSpace1DEditor::_file_opened(const String &p_file) { + + file_loaded = ResourceLoader::load(p_file); + if (file_loaded.is_valid()) { + _add_menu_type(MENU_LOAD_FILE_CONFIRM); + } +} + void AnimationNodeBlendSpace1DEditor::_add_menu_type(int p_index) { - String type = menu->get_item_metadata(p_index); + Ref<AnimationRootNode> node; + if (p_index == MENU_LOAD_FILE) { + + open_file->clear_filters(); + List<String> filters; + ResourceLoader::get_recognized_extensions_for_type("AnimationRootNode", &filters); + for (List<String>::Element *E = filters.front(); E; E = E->next()) { + open_file->add_filter("*." + E->get()); + } + open_file->popup_centered_ratio(); + return; + } else if (p_index == MENU_LOAD_FILE_CONFIRM) { + node = file_loaded; + file_loaded.unref(); + } else if (p_index == MENU_PASTE) { + + node = EditorSettings::get_singleton()->get_resource_clipboard(); + } else { + String type = menu->get_item_metadata(p_index); - Object *obj = ClassDB::instance(type); - ERR_FAIL_COND(!obj); - AnimationNode *an = Object::cast_to<AnimationNode>(obj); - ERR_FAIL_COND(!an); + Object *obj = ClassDB::instance(type); + ERR_FAIL_COND(!obj); + AnimationNode *an = Object::cast_to<AnimationNode>(obj); + ERR_FAIL_COND(!an); - Ref<AnimationNode> node(an); + node = Ref<AnimationNode>(an); + } + + if (!node.is_valid()) { + EditorNode::get_singleton()->show_warning(TTR("This type of node can't be used. Only root nodes are allowed.")); + return; + } updating = true; undo_redo->create_action("Add Node Point"); @@ -438,7 +445,7 @@ void AnimationNodeBlendSpace1DEditor::_update_tool_erase() { if (point_valid) { Ref<AnimationNode> an = blend_space->get_blend_point_node(selected_point); - if (EditorNode::get_singleton()->item_has_editor(an.ptr())) { + if (AnimationTreeEditor::get_singleton()->can_edit(an)) { open_editor->show(); } else { open_editor->hide(); @@ -490,17 +497,11 @@ void AnimationNodeBlendSpace1DEditor::_open_editor() { if (selected_point >= 0 && selected_point < blend_space->get_blend_point_count()) { Ref<AnimationNode> an = blend_space->get_blend_point_node(selected_point); ERR_FAIL_COND(an.is_null()); - EditorNode::get_singleton()->edit_item(an.ptr()); + AnimationTreeEditor::get_singleton()->enter_editor(itos(selected_point)); } } -void AnimationNodeBlendSpace1DEditor::_goto_parent() { - EditorNode::get_singleton()->edit_item(blend_space->get_parent().ptr()); -} -void AnimationNodeBlendSpace1DEditor::_removed_from_graph() { - EditorNode::get_singleton()->edit_item(NULL); -} void AnimationNodeBlendSpace1DEditor::_notification(int p_what) { if (p_what == NOTIFICATION_ENTER_TREE || p_what == NOTIFICATION_THEME_CHANGED) { @@ -513,18 +514,16 @@ void AnimationNodeBlendSpace1DEditor::_notification(int p_what) { tool_erase->set_icon(get_icon("Remove", "EditorIcons")); snap->set_icon(get_icon("SnapGrid", "EditorIcons")); open_editor->set_icon(get_icon("Edit", "EditorIcons")); - goto_parent->set_icon(get_icon("MoveUp", "EditorIcons")); + } if (p_what == NOTIFICATION_PROCESS) { String error; - if (!blend_space->get_tree()) { - error = TTR("BlendSpace1D does not belong to an AnimationTree node."); - } else if (!blend_space->get_tree()->is_active()) { + if (!AnimationTreeEditor::get_singleton()->get_tree()->is_active()) { error = TTR("AnimationTree is inactive.\nActivate to enable playback, check node warnings if activation fails."); - } else if (blend_space->get_tree()->is_state_invalid()) { - error = blend_space->get_tree()->get_invalid_state_reason(); + } else if (AnimationTreeEditor::get_singleton()->get_tree()->is_state_invalid()) { + error = AnimationTreeEditor::get_singleton()->get_tree()->get_invalid_state_reason(); } if (error != error_label->get_text()) { @@ -536,6 +535,10 @@ void AnimationNodeBlendSpace1DEditor::_notification(int p_what) { } } } + + if (p_what==NOTIFICATION_VISIBILITY_CHANGED) { + set_process(is_visible_in_tree()); + } } void AnimationNodeBlendSpace1DEditor::_bind_methods() { @@ -556,28 +559,26 @@ void AnimationNodeBlendSpace1DEditor::_bind_methods() { ClassDB::bind_method("_update_edited_point_pos", &AnimationNodeBlendSpace1DEditor::_update_edited_point_pos); ClassDB::bind_method("_open_editor", &AnimationNodeBlendSpace1DEditor::_open_editor); - ClassDB::bind_method("_goto_parent", &AnimationNodeBlendSpace1DEditor::_goto_parent); - ClassDB::bind_method("_removed_from_graph", &AnimationNodeBlendSpace1DEditor::_removed_from_graph); + ClassDB::bind_method("_file_opened", &AnimationNodeBlendSpace1DEditor::_file_opened); + + + } -void AnimationNodeBlendSpace1DEditor::edit(AnimationNodeBlendSpace1D *p_blend_space) { +bool AnimationNodeBlendSpace1DEditor::can_edit(const Ref<AnimationNode> &p_node) { - if (blend_space.is_valid()) { - blend_space->disconnect("removed_from_graph", this, "_removed_from_graph"); - } + Ref<AnimationNodeBlendSpace1D> b1d=p_node; + return b1d.is_valid(); +} - if (p_blend_space) { - blend_space = Ref<AnimationNodeBlendSpace1D>(p_blend_space); - } else { - blend_space.unref(); - } +void AnimationNodeBlendSpace1DEditor::edit(const Ref<AnimationNode> &p_node) { - if (blend_space.is_null()) { - hide(); - } else { - blend_space->connect("removed_from_graph", this, "_removed_from_graph"); + + blend_space=p_node; + + if (!blend_space.is_null()) { _update_space(); } } @@ -594,14 +595,6 @@ AnimationNodeBlendSpace1DEditor::AnimationNodeBlendSpace1DEditor() { Ref<ButtonGroup> bg; bg.instance(); - goto_parent_hb = memnew(HBoxContainer); - top_hb->add_child(goto_parent_hb); - - goto_parent = memnew(ToolButton); - goto_parent->connect("pressed", this, "_goto_parent", varray(), CONNECT_DEFERRED); - goto_parent_hb->add_child(goto_parent); - goto_parent_hb->add_child(memnew(VSeparator)); - goto_parent_hb->hide(); tool_blend = memnew(ToolButton); tool_blend->set_toggle_mode(true); @@ -726,13 +719,20 @@ AnimationNodeBlendSpace1DEditor::AnimationNodeBlendSpace1DEditor() { menu = memnew(PopupMenu); add_child(menu); - menu->connect("index_pressed", this, "_add_menu_type"); + menu->connect("id_pressed", this, "_add_menu_type"); animations_menu = memnew(PopupMenu); menu->add_child(animations_menu); animations_menu->set_name("animations"); animations_menu->connect("index_pressed", this, "_add_animation_type"); + open_file = memnew(EditorFileDialog); + add_child(open_file); + open_file->set_title(TTR("Open Animation Node")); + open_file->set_mode(EditorFileDialog::MODE_OPEN_FILE); + open_file->connect("file_selected", this, "_file_opened"); + undo_redo = EditorNode::get_singleton()->get_undo_redo(); + selected_point = -1; dragging_selected = false; dragging_selected_attempt = false; diff --git a/editor/plugins/animation_blend_space_1d_editor.h b/editor/plugins/animation_blend_space_1d_editor.h index 52139626e6..f040f6dcab 100644 --- a/editor/plugins/animation_blend_space_1d_editor.h +++ b/editor/plugins/animation_blend_space_1d_editor.h @@ -9,10 +9,11 @@ #include "scene/gui/graph_edit.h" #include "scene/gui/popup.h" #include "scene/gui/tree.h" +#include "editor/plugins/animation_tree_editor_plugin.h" -class AnimationNodeBlendSpace1DEditor : public VBoxContainer { +class AnimationNodeBlendSpace1DEditor : public AnimationTreeNodeEditorPlugin { - GDCLASS(AnimationNodeBlendSpace1DEditor, VBoxContainer) + GDCLASS(AnimationNodeBlendSpace1DEditor, AnimationTreeNodeEditorPlugin) Ref<AnimationNodeBlendSpace1D> blend_space; @@ -81,7 +82,17 @@ class AnimationNodeBlendSpace1DEditor : public VBoxContainer { void _goto_parent(); - void _removed_from_graph(); + EditorFileDialog *open_file; + Ref<AnimationNode> file_loaded; + void _file_opened(const String &p_file); + + enum { + MENU_LOAD_FILE = 1000, + MENU_PASTE = 1001, + MENU_LOAD_FILE_CONFIRM = 1002 + }; + + StringName get_blend_position_path() const; protected: void _notification(int p_what); @@ -89,29 +100,9 @@ protected: public: static AnimationNodeBlendSpace1DEditor *get_singleton() { return singleton; } - void edit(AnimationNodeBlendSpace1D *p_blend_space); + virtual bool can_edit(const Ref<AnimationNode> &p_node); + virtual void edit(const Ref<AnimationNode> &p_node); AnimationNodeBlendSpace1DEditor(); }; -class AnimationNodeBlendSpace1DEditorPlugin : public EditorPlugin { - - GDCLASS(AnimationNodeBlendSpace1DEditorPlugin, EditorPlugin) - - AnimationNodeBlendSpace1DEditor *anim_tree_editor; - EditorNode *editor; - Button *button; - -public: - virtual String get_name() const { return "BlendSpace1D"; } - - bool has_main_screen() const { return false; } - - virtual void edit(Object *p_object); - virtual bool handles(Object *p_object) const; - virtual void make_visible(bool p_visible); - - AnimationNodeBlendSpace1DEditorPlugin(EditorNode *p_node); - ~AnimationNodeBlendSpace1DEditorPlugin(); -}; - #endif // ANIMATION_BLEND_SPACE_1D_EDITOR_H diff --git a/editor/plugins/animation_blend_space_2d_editor.cpp b/editor/plugins/animation_blend_space_2d_editor.cpp index 27df60f87a..e008971e5c 100644 --- a/editor/plugins/animation_blend_space_2d_editor.cpp +++ b/editor/plugins/animation_blend_space_2d_editor.cpp @@ -11,27 +11,26 @@ #include "scene/gui/panel.h" #include "scene/main/viewport.h" -void AnimationNodeBlendSpace2DEditor::edit(AnimationNodeBlendSpace2D *p_blend_space) { +bool AnimationNodeBlendSpace2DEditor::can_edit(const Ref<AnimationNode> &p_node) { - if (blend_space.is_valid()) { - blend_space->disconnect("removed_from_graph", this, "_removed_from_graph"); - } + Ref<AnimationNodeBlendSpace2D> bs2d=p_node; + return bs2d.is_valid(); +} - if (p_blend_space) { - blend_space = Ref<AnimationNodeBlendSpace2D>(p_blend_space); - } else { - blend_space.unref(); - } +void AnimationNodeBlendSpace2DEditor::edit(const Ref<AnimationNode> &p_node) { - if (blend_space.is_null()) { - hide(); - } else { - blend_space->connect("removed_from_graph", this, "_removed_from_graph"); + blend_space = p_node; + if (!blend_space.is_null()) { _update_space(); } } +StringName AnimationNodeBlendSpace2DEditor::get_blend_position_path() const { + StringName path = AnimationTreeEditor::get_singleton()->get_base_path()+"blend_position"; + return path; +} + void AnimationNodeBlendSpace2DEditor::_blend_space_gui_input(const Ref<InputEvent> &p_event) { Ref<InputEventKey> k = p_event; @@ -54,7 +53,7 @@ void AnimationNodeBlendSpace2DEditor::_blend_space_gui_input(const Ref<InputEven ClassDB::get_inheriters_from_class("AnimationRootNode", &classes); menu->add_submenu_item(TTR("Add Animation"), "animations"); - AnimationTree *gp = blend_space->get_tree(); + AnimationTree *gp = AnimationTreeEditor::get_singleton()->get_tree(); ERR_FAIL_COND(!gp); if (gp && gp->has_node(gp->get_animation_player())) { AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(gp->get_node(gp->get_animation_player())); @@ -74,10 +73,19 @@ void AnimationNodeBlendSpace2DEditor::_blend_space_gui_input(const Ref<InputEven if (name == "Animation") continue; // nope int idx = menu->get_item_count(); - menu->add_item(vformat("Add %s", name)); + menu->add_item(vformat("Add %s", name),idx); menu->set_item_metadata(idx, E->get()); } + Ref<AnimationNode> clipb = EditorSettings::get_singleton()->get_resource_clipboard(); + if (clipb.is_valid()) { + menu->add_separator(); + menu->add_item(TTR("Paste"), MENU_PASTE); + } + menu->add_separator(); + menu->add_item(TTR("Load.."), MENU_LOAD_FILE); + + menu->set_global_position(blend_space_draw->get_global_transform().xform(mb->get_position())); menu->popup(); add_point_pos = (mb->get_position() / blend_space_draw->get_size()); @@ -203,7 +211,8 @@ void AnimationNodeBlendSpace2DEditor::_blend_space_gui_input(const Ref<InputEven blend_pos *= (blend_space->get_max_space() - blend_space->get_min_space()); blend_pos += blend_space->get_min_space(); - blend_space->set_blend_position(blend_pos); + AnimationTreeEditor::get_singleton()->get_tree()->set(get_blend_position_path(),blend_pos); + blend_space_draw->update(); } @@ -237,21 +246,54 @@ void AnimationNodeBlendSpace2DEditor::_blend_space_gui_input(const Ref<InputEven blend_pos *= (blend_space->get_max_space() - blend_space->get_min_space()); blend_pos += blend_space->get_min_space(); - blend_space->set_blend_position(blend_pos); + AnimationTreeEditor::get_singleton()->get_tree()->set(get_blend_position_path(),blend_pos); + blend_space_draw->update(); } } +void AnimationNodeBlendSpace2DEditor::_file_opened(const String &p_file) { + + file_loaded = ResourceLoader::load(p_file); + if (file_loaded.is_valid()) { + _add_menu_type(MENU_LOAD_FILE_CONFIRM); + } +} + void AnimationNodeBlendSpace2DEditor::_add_menu_type(int p_index) { - String type = menu->get_item_metadata(p_index); + Ref<AnimationRootNode> node; + if (p_index == MENU_LOAD_FILE) { + + open_file->clear_filters(); + List<String> filters; + ResourceLoader::get_recognized_extensions_for_type("AnimationRootNode", &filters); + for (List<String>::Element *E = filters.front(); E; E = E->next()) { + open_file->add_filter("*." + E->get()); + } + open_file->popup_centered_ratio(); + return; + } else if (p_index == MENU_LOAD_FILE_CONFIRM) { + node = file_loaded; + file_loaded.unref(); + } else if (p_index == MENU_PASTE) { + + node = EditorSettings::get_singleton()->get_resource_clipboard(); + } else { + String type = menu->get_item_metadata(p_index); + + Object *obj = ClassDB::instance(type); + ERR_FAIL_COND(!obj); + AnimationNode *an = Object::cast_to<AnimationNode>(obj); + ERR_FAIL_COND(!an); - Object *obj = ClassDB::instance(type); - ERR_FAIL_COND(!obj); - AnimationNode *an = Object::cast_to<AnimationNode>(obj); - ERR_FAIL_COND(!an); + node = Ref<AnimationNode>(an); + } - Ref<AnimationNode> node(an); + if (!node.is_valid()) { + EditorNode::get_singleton()->show_warning(TTR("This type of node can't be used. Only root nodes are allowed.")); + return; + } updating = true; undo_redo->create_action("Add Node Point"); @@ -288,7 +330,7 @@ void AnimationNodeBlendSpace2DEditor::_update_tool_erase() { tool_erase->set_disabled(!(selected_point >= 0 && selected_point < blend_space->get_blend_point_count()) && !(selected_triangle >= 0 && selected_triangle < blend_space->get_triangle_count())); if (selected_point >= 0 && selected_point < blend_space->get_blend_point_count()) { Ref<AnimationNode> an = blend_space->get_blend_point_node(selected_point); - if (EditorNode::get_singleton()->item_has_editor(an.ptr())) { + if (AnimationTreeEditor::get_singleton()->can_edit(an)) { open_editor->show(); } else { open_editor->hide(); @@ -490,13 +532,15 @@ void AnimationNodeBlendSpace2DEditor::_blend_space_draw() { color.a *= 0.5; } - Vector2 point = blend_space->get_blend_position(); + Vector2 blend_pos = AnimationTreeEditor::get_singleton()->get_tree()->get(get_blend_position_path()); + Vector2 point = blend_pos; + point = (point - blend_space->get_min_space()) / (blend_space->get_max_space() - blend_space->get_min_space()); point *= s; point.y = s.height - point.y; if (blend_space->get_triangle_count()) { - Vector2 closest = blend_space->get_closest_point(blend_space->get_blend_position()); + Vector2 closest = blend_space->get_closest_point(blend_pos); closest = (closest - blend_space->get_min_space()) / (blend_space->get_max_space() - blend_space->get_min_space()); closest *= s; closest.y = s.height - closest.y; @@ -527,12 +571,6 @@ void AnimationNodeBlendSpace2DEditor::_update_space() { updating = true; - if (blend_space->get_parent().is_valid()) { - goto_parent_hb->show(); - } else { - goto_parent_hb->hide(); - } - if (blend_space->get_auto_triangles()) { tool_triangle->hide(); } else { @@ -685,7 +723,6 @@ void AnimationNodeBlendSpace2DEditor::_notification(int p_what) { tool_erase->set_icon(get_icon("Remove", "EditorIcons")); snap->set_icon(get_icon("SnapGrid", "EditorIcons")); open_editor->set_icon(get_icon("Edit", "EditorIcons")); - goto_parent->set_icon(get_icon("MoveUp", "EditorIcons")); auto_triangles->set_icon(get_icon("AutoTriangle", "EditorIcons")); } @@ -693,12 +730,12 @@ void AnimationNodeBlendSpace2DEditor::_notification(int p_what) { String error; - if (!blend_space->get_tree()) { + if (!AnimationTreeEditor::get_singleton()->get_tree()) { error = TTR("BlendSpace2D does not belong to an AnimationTree node."); - } else if (!blend_space->get_tree()->is_active()) { + } else if (!AnimationTreeEditor::get_singleton()->get_tree()->is_active()) { error = TTR("AnimationTree is inactive.\nActivate to enable playback, check node warnings if activation fails."); - } else if (blend_space->get_tree()->is_state_invalid()) { - error = blend_space->get_tree()->get_invalid_state_reason(); + } else if (AnimationTreeEditor::get_singleton()->get_tree()->is_state_invalid()) { + error = AnimationTreeEditor::get_singleton()->get_tree()->get_invalid_state_reason(); } else if (blend_space->get_triangle_count() == 0) { error = TTR("No triangles exist, so no blending can take place."); } @@ -712,22 +749,22 @@ void AnimationNodeBlendSpace2DEditor::_notification(int p_what) { } } } + + if (p_what==NOTIFICATION_VISIBILITY_CHANGED) { + set_process(is_visible_in_tree()); + } } + void AnimationNodeBlendSpace2DEditor::_open_editor() { if (selected_point >= 0 && selected_point < blend_space->get_blend_point_count()) { Ref<AnimationNode> an = blend_space->get_blend_point_node(selected_point); - ERR_FAIL_COND(!an.is_valid()); - EditorNode::get_singleton()->edit_item(an.ptr()); + ERR_FAIL_COND(an.is_null()); + AnimationTreeEditor::get_singleton()->enter_editor(itos(selected_point)); } } -void AnimationNodeBlendSpace2DEditor::_goto_parent() { - - EditorNode::get_singleton()->edit_item(blend_space->get_parent().ptr()); -} - void AnimationNodeBlendSpace2DEditor::_removed_from_graph() { EditorNode::get_singleton()->edit_item(NULL); } @@ -761,11 +798,13 @@ void AnimationNodeBlendSpace2DEditor::_bind_methods() { ClassDB::bind_method("_update_edited_point_pos", &AnimationNodeBlendSpace2DEditor::_update_edited_point_pos); ClassDB::bind_method("_open_editor", &AnimationNodeBlendSpace2DEditor::_open_editor); - ClassDB::bind_method("_goto_parent", &AnimationNodeBlendSpace2DEditor::_goto_parent); ClassDB::bind_method("_removed_from_graph", &AnimationNodeBlendSpace2DEditor::_removed_from_graph); ClassDB::bind_method("_auto_triangles_toggled", &AnimationNodeBlendSpace2DEditor::_auto_triangles_toggled); + + ClassDB::bind_method("_file_opened", &AnimationNodeBlendSpace2DEditor::_file_opened); + } AnimationNodeBlendSpace2DEditor *AnimationNodeBlendSpace2DEditor::singleton = NULL; @@ -781,14 +820,6 @@ AnimationNodeBlendSpace2DEditor::AnimationNodeBlendSpace2DEditor() { Ref<ButtonGroup> bg; bg.instance(); - goto_parent_hb = memnew(HBoxContainer); - top_hb->add_child(goto_parent_hb); - goto_parent = memnew(ToolButton); - goto_parent->connect("pressed", this, "_goto_parent", varray(), CONNECT_DEFERRED); - goto_parent_hb->add_child(goto_parent); - goto_parent_hb->add_child(memnew(VSeparator)); - goto_parent_hb->hide(); - tool_blend = memnew(ToolButton); tool_blend->set_toggle_mode(true); tool_blend->set_button_group(bg); @@ -968,13 +999,20 @@ AnimationNodeBlendSpace2DEditor::AnimationNodeBlendSpace2DEditor() { menu = memnew(PopupMenu); add_child(menu); - menu->connect("index_pressed", this, "_add_menu_type"); + menu->connect("id_pressed", this, "_add_menu_type"); animations_menu = memnew(PopupMenu); menu->add_child(animations_menu); animations_menu->set_name("animations"); animations_menu->connect("index_pressed", this, "_add_animation_type"); + open_file = memnew(EditorFileDialog); + add_child(open_file); + open_file->set_title(TTR("Open Animation Node")); + open_file->set_mode(EditorFileDialog::MODE_OPEN_FILE); + open_file->connect("file_selected", this, "_file_opened"); + undo_redo = EditorNode::get_singleton()->get_undo_redo(); + selected_point = -1; selected_triangle = -1; @@ -982,42 +1020,3 @@ AnimationNodeBlendSpace2DEditor::AnimationNodeBlendSpace2DEditor() { dragging_selected_attempt = false; } -void AnimationNodeBlendSpace2DEditorPlugin::edit(Object *p_object) { - - anim_tree_editor->edit(Object::cast_to<AnimationNodeBlendSpace2D>(p_object)); -} - -bool AnimationNodeBlendSpace2DEditorPlugin::handles(Object *p_object) const { - - return p_object->is_class("AnimationNodeBlendSpace2D"); -} - -void AnimationNodeBlendSpace2DEditorPlugin::make_visible(bool p_visible) { - - if (p_visible) { - //editor->hide_animation_player_editors(); - //editor->animation_panel_make_visible(true); - button->show(); - editor->make_bottom_panel_item_visible(anim_tree_editor); - anim_tree_editor->set_process(true); - } else { - - if (anim_tree_editor->is_visible_in_tree()) - editor->hide_bottom_panel(); - button->hide(); - anim_tree_editor->set_process(false); - } -} - -AnimationNodeBlendSpace2DEditorPlugin::AnimationNodeBlendSpace2DEditorPlugin(EditorNode *p_node) { - - editor = p_node; - anim_tree_editor = memnew(AnimationNodeBlendSpace2DEditor); - anim_tree_editor->set_custom_minimum_size(Size2(0, 300)); - - button = editor->add_bottom_panel_item(TTR("BlendSpace2D"), anim_tree_editor); - button->hide(); -} - -AnimationNodeBlendSpace2DEditorPlugin::~AnimationNodeBlendSpace2DEditorPlugin() { -} diff --git a/editor/plugins/animation_blend_space_2d_editor.h b/editor/plugins/animation_blend_space_2d_editor.h index a0e497804e..ae684985f3 100644 --- a/editor/plugins/animation_blend_space_2d_editor.h +++ b/editor/plugins/animation_blend_space_2d_editor.h @@ -9,18 +9,17 @@ #include "scene/gui/graph_edit.h" #include "scene/gui/popup.h" #include "scene/gui/tree.h" +#include "editor/plugins/animation_tree_editor_plugin.h" /** @author Juan Linietsky <reduzio@gmail.com> */ -class AnimationNodeBlendSpace2DEditor : public VBoxContainer { +class AnimationNodeBlendSpace2DEditor : public AnimationTreeNodeEditorPlugin { - GDCLASS(AnimationNodeBlendSpace2DEditor, VBoxContainer); + GDCLASS(AnimationNodeBlendSpace2DEditor, AnimationTreeNodeEditorPlugin); Ref<AnimationNodeBlendSpace2D> blend_space; - HBoxContainer *goto_parent_hb; - ToolButton *goto_parent; PanelContainer *panel; ToolButton *tool_blend; @@ -93,38 +92,32 @@ class AnimationNodeBlendSpace2DEditor : public VBoxContainer { void _edit_point_pos(double); void _open_editor(); - void _goto_parent(); - void _removed_from_graph(); void _auto_triangles_toggled(); + StringName get_blend_position_path() const; + + EditorFileDialog *open_file; + Ref<AnimationNode> file_loaded; + void _file_opened(const String &p_file); + + enum { + MENU_LOAD_FILE = 1000, + MENU_PASTE = 1001, + MENU_LOAD_FILE_CONFIRM = 1002 + }; + protected: void _notification(int p_what); static void _bind_methods(); public: static AnimationNodeBlendSpace2DEditor *get_singleton() { return singleton; } - void edit(AnimationNodeBlendSpace2D *p_blend_space); + virtual bool can_edit(const Ref<AnimationNode> &p_node); + virtual void edit(const Ref<AnimationNode> &p_node); AnimationNodeBlendSpace2DEditor(); }; -class AnimationNodeBlendSpace2DEditorPlugin : public EditorPlugin { - GDCLASS(AnimationNodeBlendSpace2DEditorPlugin, EditorPlugin); - - AnimationNodeBlendSpace2DEditor *anim_tree_editor; - EditorNode *editor; - Button *button; - -public: - virtual String get_name() const { return "BlendSpace2D"; } - bool has_main_screen() const { return false; } - virtual void edit(Object *p_object); - virtual bool handles(Object *p_object) const; - virtual void make_visible(bool p_visible); - - AnimationNodeBlendSpace2DEditorPlugin(EditorNode *p_node); - ~AnimationNodeBlendSpace2DEditorPlugin(); -}; #endif // ANIMATION_BLEND_SPACE_2D_EDITOR_H diff --git a/editor/plugins/animation_blend_tree_editor_plugin.cpp b/editor/plugins/animation_blend_tree_editor_plugin.cpp index c00ad451fa..42e32b9788 100644 --- a/editor/plugins/animation_blend_tree_editor_plugin.cpp +++ b/editor/plugins/animation_blend_tree_editor_plugin.cpp @@ -2,6 +2,7 @@ #include "core/io/resource_loader.h" #include "core/project_settings.h" +#include "editor/editor_inspector.h" #include "os/input.h" #include "os/keyboard.h" #include "scene/animation/animation_player.h" @@ -9,27 +10,6 @@ #include "scene/gui/panel.h" #include "scene/main/viewport.h" -void AnimationNodeBlendTreeEditor::edit(AnimationNodeBlendTree *p_blend_tree) { - - if (blend_tree.is_valid()) { - blend_tree->disconnect("removed_from_graph", this, "_removed_from_graph"); - } - - if (p_blend_tree) { - blend_tree = Ref<AnimationNodeBlendTree>(p_blend_tree); - } else { - blend_tree.unref(); - } - - if (blend_tree.is_null()) { - hide(); - } else { - blend_tree->connect("removed_from_graph", this, "_removed_from_graph"); - - _update_graph(); - } -} - void AnimationNodeBlendTreeEditor::add_custom_type(const String &p_name, const Ref<Script> &p_script) { for (int i = 0; i < add_options.size(); i++) { @@ -58,10 +38,19 @@ void AnimationNodeBlendTreeEditor::remove_custom_type(const Ref<Script> &p_scrip void AnimationNodeBlendTreeEditor::_update_options_menu() { + print_line("update options"); add_node->get_popup()->clear(); for (int i = 0; i < add_options.size(); i++) { - add_node->get_popup()->add_item(add_options[i].name); + add_node->get_popup()->add_item(add_options[i].name, i); } + + Ref<AnimationNode> clipb = EditorSettings::get_singleton()->get_resource_clipboard(); + if (clipb.is_valid()) { + add_node->get_popup()->add_separator(); + add_node->get_popup()->add_item(TTR("Paste"), MENU_PASTE); + } + add_node->get_popup()->add_separator(); + add_node->get_popup()->add_item(TTR("Load.."), MENU_LOAD_FILE); } Size2 AnimationNodeBlendTreeEditor::get_minimum_size() const { @@ -69,18 +58,28 @@ Size2 AnimationNodeBlendTreeEditor::get_minimum_size() const { return Size2(10, 200); } +void AnimationNodeBlendTreeEditor::_property_changed(const StringName &p_property, const Variant &p_value) { + + AnimationTree *tree = AnimationTreeEditor::get_singleton()->get_tree(); + updating = true; + undo_redo->create_action("Parameter Changed: " + String(p_property), UndoRedo::MERGE_ENDS); + undo_redo->add_do_property(tree, p_property, p_value); + undo_redo->add_undo_property(tree, p_property, tree->get(p_property)); + undo_redo->add_do_method(this, "_update_graph"); + undo_redo->add_undo_method(this, "_update_graph"); + undo_redo->commit_action(); + updating = false; +} + void AnimationNodeBlendTreeEditor::_update_graph() { if (updating) return; + visible_properties.clear(); + graph->set_scroll_ofs(blend_tree->get_graph_offset() * EDSCALE); - if (blend_tree->get_parent().is_valid()) { - goto_parent->show(); - } else { - goto_parent->hide(); - } graph->clear_connections(); //erase all nodes for (int i = 0; i < graph->get_child_count(); i++) { @@ -107,7 +106,7 @@ void AnimationNodeBlendTreeEditor::_update_graph() { agnode->connect("changed", this, "_node_changed", varray(agnode->get_instance_id()), CONNECT_DEFERRED); } - node->set_offset(agnode->get_position() * EDSCALE); + node->set_offset(blend_tree->get_node_position(E->get()) * EDSCALE); node->set_title(agnode->get_caption()); node->set_name(E->get()); @@ -133,9 +132,28 @@ void AnimationNodeBlendTreeEditor::_update_graph() { node->set_slot(base + i, true, 0, get_color("font_color", "Label"), false, 0, Color()); } - node->connect("dragged", this, "_node_dragged", varray(agnode)); + List<PropertyInfo> pinfo; + agnode->get_parameter_list(&pinfo); + for (List<PropertyInfo>::Element *F = pinfo.front(); F; F = F->next()) { + + if (!(F->get().usage & PROPERTY_USAGE_EDITOR)) { + continue; + } + String base_path = AnimationTreeEditor::get_singleton()->get_base_path() + String(E->get()) + "/" + F->get().name; + EditorProperty *prop = EditorInspector::instantiate_property_editor(AnimationTreeEditor::get_singleton()->get_tree(), F->get().type, base_path, F->get().hint, F->get().hint_string, F->get().usage); + if (prop) { + prop->set_object_and_property(AnimationTreeEditor::get_singleton()->get_tree(), base_path); + prop->update_property(); + prop->set_name_split_ratio(0); + prop->connect("property_changed", this, "_property_changed"); + node->add_child(prop); + visible_properties.push_back(prop); + } + } + + node->connect("dragged", this, "_node_dragged", varray(E->get())); - if (EditorNode::get_singleton()->item_has_editor(agnode.ptr())) { + if (AnimationTreeEditor::get_singleton()->can_edit(agnode)) { node->add_child(memnew(HSeparator)); Button *open_in_editor = memnew(Button); open_in_editor->set_text(TTR("Open Editor")); @@ -169,7 +187,7 @@ void AnimationNodeBlendTreeEditor::_update_graph() { ProgressBar *pb = memnew(ProgressBar); - AnimationTree *player = anim->get_tree(); + AnimationTree *player = AnimationTreeEditor::get_singleton()->get_tree(); if (player->has_node(player->get_animation_player())) { AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(player->get_node(player->get_animation_player())); if (ap) { @@ -194,6 +212,7 @@ void AnimationNodeBlendTreeEditor::_update_graph() { mb->get_popup()->connect("index_pressed", this, "_anim_selected", varray(options, E->get()), CONNECT_DEFERRED); } + /* should be no longer necesary, as the boolean works Ref<AnimationNodeOneShot> oneshot = agnode; if (oneshot.is_valid()) { @@ -209,7 +228,7 @@ void AnimationNodeBlendTreeEditor::_update_graph() { play_stop->add_child(stop); play_stop->add_spacer(); node->add_child(play_stop); - } + } */ } List<AnimationNodeBlendTree::NodeConnection> connections; @@ -225,16 +244,44 @@ void AnimationNodeBlendTreeEditor::_update_graph() { } } -void AnimationNodeBlendTreeEditor::_add_node(int p_idx) { +void AnimationNodeBlendTreeEditor::_file_opened(const String &p_file) { - ERR_FAIL_INDEX(p_idx, add_options.size()); + file_loaded = ResourceLoader::load(p_file); + if (file_loaded.is_valid()) { + _add_node(MENU_LOAD_FILE_CONFIRM); + } +} + +void AnimationNodeBlendTreeEditor::_add_node(int p_idx) { Ref<AnimationNode> anode; - if (add_options[p_idx].type != String()) { + String base_name; + + if (p_idx == MENU_LOAD_FILE) { + + open_file->clear_filters(); + List<String> filters; + ResourceLoader::get_recognized_extensions_for_type("AnimationNode", &filters); + for (List<String>::Element *E = filters.front(); E; E = E->next()) { + open_file->add_filter("*." + E->get()); + } + open_file->popup_centered_ratio(); + return; + } else if (p_idx == MENU_LOAD_FILE_CONFIRM) { + anode = file_loaded; + file_loaded.unref(); + base_name = anode->get_class(); + } else if (p_idx == MENU_PASTE) { + + anode = EditorSettings::get_singleton()->get_resource_clipboard(); + ERR_FAIL_COND(!anode.is_valid()); + base_name = anode->get_class(); + } else if (add_options[p_idx].type != String()) { AnimationNode *an = Object::cast_to<AnimationNode>(ClassDB::instance(add_options[p_idx].type)); ERR_FAIL_COND(!an); anode = Ref<AnimationNode>(an); + base_name = add_options[p_idx].name; } else { ERR_FAIL_COND(add_options[p_idx].script.is_null()); String base_type = add_options[p_idx].script->get_instance_base_type(); @@ -242,13 +289,16 @@ void AnimationNodeBlendTreeEditor::_add_node(int p_idx) { ERR_FAIL_COND(!an); anode = Ref<AnimationNode>(an); anode->set_script(add_options[p_idx].script.get_ref_ptr()); + base_name = add_options[p_idx].name; } + Ref<AnimationNodeOutput> out = anode; + if (out.is_valid()) { + EditorNode::get_singleton()->show_warning(TTR("Output node can't be added to the blend tree.")); + return; + } Point2 instance_pos = graph->get_scroll_ofs() + graph->get_size() * 0.5; - anode->set_position(instance_pos / EDSCALE); - - String base_name = add_options[p_idx].name; int base = 1; String name = base_name; while (blend_tree->has_node(name)) { @@ -257,19 +307,19 @@ void AnimationNodeBlendTreeEditor::_add_node(int p_idx) { } undo_redo->create_action("Add Node to BlendTree"); - undo_redo->add_do_method(blend_tree.ptr(), "add_node", name, anode); + undo_redo->add_do_method(blend_tree.ptr(), "add_node", name, anode, instance_pos / EDSCALE); undo_redo->add_undo_method(blend_tree.ptr(), "remove_node", name); undo_redo->add_do_method(this, "_update_graph"); undo_redo->add_undo_method(this, "_update_graph"); undo_redo->commit_action(); } -void AnimationNodeBlendTreeEditor::_node_dragged(const Vector2 &p_from, const Vector2 &p_to, Ref<AnimationNode> p_node) { +void AnimationNodeBlendTreeEditor::_node_dragged(const Vector2 &p_from, const Vector2 &p_to, const StringName &p_which) { updating = true; undo_redo->create_action("Node Moved"); - undo_redo->add_do_method(p_node.ptr(), "set_position", p_to / EDSCALE); - undo_redo->add_undo_method(p_node.ptr(), "set_position", p_from / EDSCALE); + undo_redo->add_do_method(blend_tree.ptr(), "set_node_position", p_which, p_to / EDSCALE); + undo_redo->add_undo_method(blend_tree.ptr(), "set_node_position", p_which, p_from / EDSCALE); undo_redo->add_do_method(this, "_update_graph"); undo_redo->add_undo_method(this, "_update_graph"); undo_redo->commit_action(); @@ -342,20 +392,6 @@ void AnimationNodeBlendTreeEditor::_delete_request(const String &p_which) { undo_redo->commit_action(); } -void AnimationNodeBlendTreeEditor::_oneshot_start(const StringName &p_name) { - - Ref<AnimationNodeOneShot> os = blend_tree->get_node(p_name); - ERR_FAIL_COND(!os.is_valid()); - os->start(); -} - -void AnimationNodeBlendTreeEditor::_oneshot_stop(const StringName &p_name) { - - Ref<AnimationNodeOneShot> os = blend_tree->get_node(p_name); - ERR_FAIL_COND(!os.is_valid()); - os->stop(); -} - void AnimationNodeBlendTreeEditor::_node_selected(Object *p_node) { GraphNode *gn = Object::cast_to<GraphNode>(p_node); @@ -373,13 +409,7 @@ void AnimationNodeBlendTreeEditor::_open_in_editor(const String &p_which) { Ref<AnimationNode> an = blend_tree->get_node(p_which); ERR_FAIL_COND(!an.is_valid()) - EditorNode::get_singleton()->edit_item(an.ptr()); -} - -void AnimationNodeBlendTreeEditor::_open_parent() { - if (blend_tree->get_parent().is_valid()) { - EditorNode::get_singleton()->edit_item(blend_tree->get_parent().ptr()); - } + AnimationTreeEditor::get_singleton()->enter_editor(p_which); } void AnimationNodeBlendTreeEditor::_filter_toggled() { @@ -417,14 +447,14 @@ bool AnimationNodeBlendTreeEditor::_update_filters(const Ref<AnimationNode> &ano if (updating || _filter_edit != anode) return false; - NodePath player_path = anode->get_tree()->get_animation_player(); + NodePath player_path = AnimationTreeEditor::get_singleton()->get_tree()->get_animation_player(); - if (!anode->get_tree()->has_node(player_path)) { + if (!AnimationTreeEditor::get_singleton()->get_tree()->has_node(player_path)) { EditorNode::get_singleton()->show_warning(TTR("No animation player set, so unable to retrieve track names.")); return false; } - AnimationPlayer *player = Object::cast_to<AnimationPlayer>(anode->get_tree()->get_node(player_path)); + AnimationPlayer *player = Object::cast_to<AnimationPlayer>(AnimationTreeEditor::get_singleton()->get_tree()->get_node(player_path)); if (!player) { EditorNode::get_singleton()->show_warning(TTR("Player path set is invalid, so unable to retrieve track names.")); return false; @@ -593,8 +623,6 @@ void AnimationNodeBlendTreeEditor::_notification(int p_what) { if (p_what == NOTIFICATION_ENTER_TREE || p_what == NOTIFICATION_THEME_CHANGED) { - goto_parent->set_icon(get_icon("MoveUp", "EditorIcons")); - error_panel->add_style_override("panel", get_stylebox("bg", "Tree")); error_label->add_color_override("font_color", get_color("error_color", "Editor")); } @@ -603,12 +631,10 @@ void AnimationNodeBlendTreeEditor::_notification(int p_what) { String error; - if (!blend_tree->get_tree()) { - error = TTR("BlendTree does not belong to an AnimationTree node."); - } else if (!blend_tree->get_tree()->is_active()) { + if (!AnimationTreeEditor::get_singleton()->get_tree()->is_active()) { error = TTR("AnimationTree is inactive.\nActivate to enable playback, check node warnings if activation fails."); - } else if (blend_tree->get_tree()->is_state_invalid()) { - error = blend_tree->get_tree()->get_invalid_state_reason(); + } else if (AnimationTreeEditor::get_singleton()->get_tree()->is_state_invalid()) { + error = AnimationTreeEditor::get_singleton()->get_tree()->get_invalid_state_reason(); } if (error != error_label->get_text()) { @@ -624,13 +650,13 @@ void AnimationNodeBlendTreeEditor::_notification(int p_what) { blend_tree->get_node_connections(&conns); for (List<AnimationNodeBlendTree::NodeConnection>::Element *E = conns.front(); E; E = E->next()) { float activity = 0; - if (blend_tree->get_tree() && !blend_tree->get_tree()->is_state_invalid()) { + if (AnimationTreeEditor::get_singleton()->get_tree() && !AnimationTreeEditor::get_singleton()->get_tree()->is_state_invalid()) { activity = blend_tree->get_connection_activity(E->get().input_node, E->get().input_index); } graph->set_connection_activity(E->get().output_node, 0, E->get().input_node, E->get().input_index, activity); } - AnimationTree *graph_player = blend_tree->get_tree(); + AnimationTree *graph_player = AnimationTreeEditor::get_singleton()->get_tree(); AnimationPlayer *player = NULL; if (graph_player->has_node(graph_player->get_animation_player())) { player = Object::cast_to<AnimationPlayer>(graph_player->get_node(graph_player->get_animation_player())); @@ -650,6 +676,14 @@ void AnimationNodeBlendTreeEditor::_notification(int p_what) { } } } + + for (int i = 0; i < visible_properties.size(); i++) { + visible_properties[i]->update_property(); + } + } + + if (p_what == NOTIFICATION_VISIBILITY_CHANGED) { + set_process(is_visible_in_tree()); } } @@ -664,9 +698,9 @@ void AnimationNodeBlendTreeEditor::_scroll_changed(const Vector2 &p_scroll) { void AnimationNodeBlendTreeEditor::_node_changed(ObjectID p_node) { AnimationNode *an = Object::cast_to<AnimationNode>(ObjectDB::get_instance(p_node)); - if (an && an->get_parent() == blend_tree) { - _update_graph(); - } + //if (an && an->get_parent() == blend_tree) { + _update_graph(); + //} } void AnimationNodeBlendTreeEditor::_bind_methods() { @@ -680,17 +714,17 @@ void AnimationNodeBlendTreeEditor::_bind_methods() { ClassDB::bind_method("_disconnection_request", &AnimationNodeBlendTreeEditor::_disconnection_request); ClassDB::bind_method("_node_selected", &AnimationNodeBlendTreeEditor::_node_selected); ClassDB::bind_method("_open_in_editor", &AnimationNodeBlendTreeEditor::_open_in_editor); - ClassDB::bind_method("_open_parent", &AnimationNodeBlendTreeEditor::_open_parent); ClassDB::bind_method("_scroll_changed", &AnimationNodeBlendTreeEditor::_scroll_changed); ClassDB::bind_method("_delete_request", &AnimationNodeBlendTreeEditor::_delete_request); ClassDB::bind_method("_edit_filters", &AnimationNodeBlendTreeEditor::_edit_filters); ClassDB::bind_method("_update_filters", &AnimationNodeBlendTreeEditor::_update_filters); ClassDB::bind_method("_filter_edited", &AnimationNodeBlendTreeEditor::_filter_edited); ClassDB::bind_method("_filter_toggled", &AnimationNodeBlendTreeEditor::_filter_toggled); - ClassDB::bind_method("_oneshot_start", &AnimationNodeBlendTreeEditor::_oneshot_start); - ClassDB::bind_method("_oneshot_stop", &AnimationNodeBlendTreeEditor::_oneshot_stop); ClassDB::bind_method("_node_changed", &AnimationNodeBlendTreeEditor::_node_changed); ClassDB::bind_method("_removed_from_graph", &AnimationNodeBlendTreeEditor::_removed_from_graph); + ClassDB::bind_method("_property_changed", &AnimationNodeBlendTreeEditor::_property_changed); + ClassDB::bind_method("_file_opened", &AnimationNodeBlendTreeEditor::_file_opened); + ClassDB::bind_method("_update_options_menu", &AnimationNodeBlendTreeEditor::_update_options_menu); ClassDB::bind_method("_anim_selected", &AnimationNodeBlendTreeEditor::_anim_selected); } @@ -708,7 +742,9 @@ void AnimationNodeBlendTreeEditor::_node_renamed(const String &p_text, Ref<Anima ERR_FAIL_COND(new_name == "" || new_name.find(".") != -1 || new_name.find("/") != -1) - ERR_FAIL_COND(new_name == prev_name); + if (new_name == prev_name) { + return; //nothing to do + } String base_name = new_name; int base = 1; @@ -718,22 +754,61 @@ void AnimationNodeBlendTreeEditor::_node_renamed(const String &p_text, Ref<Anima name = base_name + " " + itos(base); } + String base_path = AnimationTreeEditor::get_singleton()->get_base_path(); + updating = true; undo_redo->create_action("Node Renamed"); undo_redo->add_do_method(blend_tree.ptr(), "rename_node", prev_name, name); undo_redo->add_undo_method(blend_tree.ptr(), "rename_node", name, prev_name); + undo_redo->add_do_method(AnimationTreeEditor::get_singleton()->get_tree(), "rename_parameter", base_path + prev_name, base_path + name); + undo_redo->add_undo_method(AnimationTreeEditor::get_singleton()->get_tree(), "rename_parameter", base_path + name, base_path + prev_name); undo_redo->add_do_method(this, "_update_graph"); undo_redo->add_undo_method(this, "_update_graph"); undo_redo->commit_action(); updating = false; gn->set_name(new_name); gn->set_size(gn->get_minimum_size()); + + //change editors accordingly + for (int i = 0; i < visible_properties.size(); i++) { + String pname = visible_properties[i]->get_edited_property().operator String(); + if (pname.begins_with(base_path + prev_name)) { + String new_name = pname.replace_first(base_path + prev_name, base_path + name); + visible_properties[i]->set_object_and_property(visible_properties[i]->get_edited_object(), new_name); + } + } } void AnimationNodeBlendTreeEditor::_node_renamed_focus_out(Node *le, Ref<AnimationNode> p_node) { _node_renamed(le->call("get_text"), p_node); } +bool AnimationNodeBlendTreeEditor::can_edit(const Ref<AnimationNode> &p_node) { + Ref<AnimationNodeBlendTree> bt = p_node; + return bt.is_valid(); +} + +void AnimationNodeBlendTreeEditor::edit(const Ref<AnimationNode> &p_node) { + + if (blend_tree.is_valid()) { + blend_tree->disconnect("removed_from_graph", this, "_removed_from_graph"); + } + + if (p_node.is_valid()) { + blend_tree = p_node; + } else { + blend_tree.unref(); + } + + if (blend_tree.is_null()) { + hide(); + } else { + blend_tree->connect("removed_from_graph", this, "_removed_from_graph"); + + _update_graph(); + } +} + AnimationNodeBlendTreeEditor::AnimationNodeBlendTreeEditor() { singleton = this; @@ -757,13 +832,8 @@ AnimationNodeBlendTreeEditor::AnimationNodeBlendTreeEditor() { graph->get_zoom_hbox()->add_child(add_node); add_node->set_text(TTR("Add Node..")); graph->get_zoom_hbox()->move_child(add_node, 0); - add_node->get_popup()->connect("index_pressed", this, "_add_node"); - - goto_parent = memnew(Button); - graph->get_zoom_hbox()->add_child(goto_parent); - graph->get_zoom_hbox()->move_child(goto_parent, 0); - goto_parent->hide(); - goto_parent->connect("pressed", this, "_open_parent"); + add_node->get_popup()->connect("id_pressed", this, "_add_node"); + add_node->connect("about_to_show", this, "_update_options_menu"); add_options.push_back(AddOption("Animation", "AnimationNodeAnimation")); add_options.push_back(AddOption("OneShot", "AnimationNodeOneShot")); @@ -804,45 +874,10 @@ AnimationNodeBlendTreeEditor::AnimationNodeBlendTreeEditor() { filters->set_hide_root(true); filters->connect("item_edited", this, "_filter_edited"); + open_file = memnew(EditorFileDialog); + add_child(open_file); + open_file->set_title(TTR("Open Animation Node")); + open_file->set_mode(EditorFileDialog::MODE_OPEN_FILE); + open_file->connect("file_selected", this, "_file_opened"); undo_redo = EditorNode::get_singleton()->get_undo_redo(); } - -void AnimationNodeBlendTreeEditorPlugin::edit(Object *p_object) { - - anim_tree_editor->edit(Object::cast_to<AnimationNodeBlendTree>(p_object)); -} - -bool AnimationNodeBlendTreeEditorPlugin::handles(Object *p_object) const { - - return p_object->is_class("AnimationNodeBlendTree"); -} - -void AnimationNodeBlendTreeEditorPlugin::make_visible(bool p_visible) { - - if (p_visible) { - //editor->hide_animation_player_editors(); - //editor->animation_panel_make_visible(true); - button->show(); - editor->make_bottom_panel_item_visible(anim_tree_editor); - anim_tree_editor->set_process(true); - } else { - - if (anim_tree_editor->is_visible_in_tree()) - editor->hide_bottom_panel(); - button->hide(); - anim_tree_editor->set_process(false); - } -} - -AnimationNodeBlendTreeEditorPlugin::AnimationNodeBlendTreeEditorPlugin(EditorNode *p_node) { - - editor = p_node; - anim_tree_editor = memnew(AnimationNodeBlendTreeEditor); - anim_tree_editor->set_custom_minimum_size(Size2(0, 300)); - - button = editor->add_bottom_panel_item(TTR("BlendTree"), anim_tree_editor); - button->hide(); -} - -AnimationNodeBlendTreeEditorPlugin::~AnimationNodeBlendTreeEditorPlugin() { -} diff --git a/editor/plugins/animation_blend_tree_editor_plugin.h b/editor/plugins/animation_blend_tree_editor_plugin.h index deba3b2b0e..35ecc32979 100644 --- a/editor/plugins/animation_blend_tree_editor_plugin.h +++ b/editor/plugins/animation_blend_tree_editor_plugin.h @@ -3,6 +3,7 @@ #include "editor/editor_node.h" #include "editor/editor_plugin.h" +#include "editor/plugins/animation_tree_editor_plugin.h" #include "editor/property_editor.h" #include "scene/animation/animation_blend_tree.h" #include "scene/gui/button.h" @@ -13,14 +14,13 @@ @author Juan Linietsky <reduzio@gmail.com> */ -class AnimationNodeBlendTreeEditor : public VBoxContainer { +class AnimationNodeBlendTreeEditor : public AnimationTreeNodeEditorPlugin { - GDCLASS(AnimationNodeBlendTreeEditor, VBoxContainer); + GDCLASS(AnimationNodeBlendTreeEditor, AnimationTreeNodeEditorPlugin); Ref<AnimationNodeBlendTree> blend_tree; GraphEdit *graph; MenuButton *add_node; - Button *goto_parent; PanelContainer *error_panel; Label *error_label; @@ -32,6 +32,7 @@ class AnimationNodeBlendTreeEditor : public VBoxContainer { CheckBox *filter_enabled; Map<StringName, ProgressBar *> animations; + Vector<EditorProperty *> visible_properties; void _update_graph(); @@ -52,7 +53,7 @@ class AnimationNodeBlendTreeEditor : public VBoxContainer { static AnimationNodeBlendTreeEditor *singleton; - void _node_dragged(const Vector2 &p_from, const Vector2 &p_to, Ref<AnimationNode> p_node); + void _node_dragged(const Vector2 &p_from, const Vector2 &p_to, const StringName &p_which); void _node_renamed(const String &p_text, Ref<AnimationNode> p_node); void _node_renamed_focus_out(Node *le, Ref<AnimationNode> p_node); @@ -64,11 +65,8 @@ class AnimationNodeBlendTreeEditor : public VBoxContainer { void _scroll_changed(const Vector2 &p_scroll); void _node_selected(Object *p_node); void _open_in_editor(const String &p_which); - void _open_parent(); void _anim_selected(int p_index, Array p_options, const String &p_node); void _delete_request(const String &p_which); - void _oneshot_start(const StringName &p_name); - void _oneshot_stop(const StringName &p_name); bool _update_filters(const Ref<AnimationNode> &anode); void _edit_filters(const String &p_which); @@ -78,8 +76,19 @@ class AnimationNodeBlendTreeEditor : public VBoxContainer { void _node_changed(ObjectID p_node); + void _property_changed(const StringName &p_property, const Variant &p_value); void _removed_from_graph(); + EditorFileDialog *open_file; + Ref<AnimationNode> file_loaded; + void _file_opened(const String &p_file); + + enum { + MENU_LOAD_FILE = 1000, + MENU_PASTE = 1001, + MENU_LOAD_FILE_CONFIRM = 1002 + }; + protected: void _notification(int p_what); static void _bind_methods(); @@ -91,27 +100,11 @@ public: void remove_custom_type(const Ref<Script> &p_script); virtual Size2 get_minimum_size() const; - void edit(AnimationNodeBlendTree *p_blend_tree); - AnimationNodeBlendTreeEditor(); -}; -class AnimationNodeBlendTreeEditorPlugin : public EditorPlugin { + virtual bool can_edit(const Ref<AnimationNode> &p_node); + virtual void edit(const Ref<AnimationNode> &p_node); - GDCLASS(AnimationNodeBlendTreeEditorPlugin, EditorPlugin); - - AnimationNodeBlendTreeEditor *anim_tree_editor; - EditorNode *editor; - Button *button; - -public: - virtual String get_name() const { return "BlendTree"; } - bool has_main_screen() const { return false; } - virtual void edit(Object *p_object); - virtual bool handles(Object *p_object) const; - virtual void make_visible(bool p_visible); - - AnimationNodeBlendTreeEditorPlugin(EditorNode *p_node); - ~AnimationNodeBlendTreeEditorPlugin(); + AnimationNodeBlendTreeEditor(); }; #endif // ANIMATION_BLEND_TREE_EDITOR_PLUGIN_H diff --git a/editor/plugins/animation_state_machine_editor.cpp b/editor/plugins/animation_state_machine_editor.cpp index ee450333c8..3a65cb9b38 100644 --- a/editor/plugins/animation_state_machine_editor.cpp +++ b/editor/plugins/animation_state_machine_editor.cpp @@ -11,22 +11,17 @@ #include "scene/gui/panel.h" #include "scene/main/viewport.h" -void AnimationNodeStateMachineEditor::edit(AnimationNodeStateMachine *p_state_machine) { +bool AnimationNodeStateMachineEditor::can_edit(const Ref<AnimationNode> &p_node) { - if (state_machine.is_valid()) { - state_machine->disconnect("removed_from_graph", this, "_removed_from_graph"); - } + Ref<AnimationNodeStateMachine> ansm = p_node; + return ansm.is_valid(); +} - if (p_state_machine) { - state_machine = Ref<AnimationNodeStateMachine>(p_state_machine); - } else { - state_machine.unref(); - } +void AnimationNodeStateMachineEditor::edit(const Ref<AnimationNode> &p_node) { - if (state_machine.is_null()) { - hide(); - } else { - state_machine->connect("removed_from_graph", this, "_removed_from_graph"); + state_machine = p_node; + + if (state_machine.is_valid()) { selected_transition_from = StringName(); selected_transition_to = StringName(); @@ -38,6 +33,10 @@ void AnimationNodeStateMachineEditor::edit(AnimationNodeStateMachine *p_state_ma void AnimationNodeStateMachineEditor::_state_machine_gui_input(const Ref<InputEvent> &p_event) { + Ref<AnimationNodeStateMachinePlayback> playback = AnimationTreeEditor::get_singleton()->get_tree()->get(AnimationTreeEditor::get_singleton()->get_base_path() + "playback"); + if (playback.is_null()) + return; + Ref<InputEventKey> k = p_event; if (tool_select->is_pressed() && k.is_valid() && k->is_pressed() && k->get_scancode() == KEY_DELETE && !k->is_echo()) { if (selected_node != StringName() || selected_transition_to != StringName() || selected_transition_from != StringName()) { @@ -59,7 +58,7 @@ void AnimationNodeStateMachineEditor::_state_machine_gui_input(const Ref<InputEv ClassDB::get_inheriters_from_class("AnimationRootNode", &classes); menu->add_submenu_item(TTR("Add Animation"), "animations"); - AnimationTree *gp = state_machine->get_tree(); + AnimationTree *gp = AnimationTreeEditor::get_singleton()->get_tree(); ERR_FAIL_COND(!gp); if (gp && gp->has_node(gp->get_animation_player())) { AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(gp->get_node(gp->get_animation_player())); @@ -79,9 +78,17 @@ void AnimationNodeStateMachineEditor::_state_machine_gui_input(const Ref<InputEv if (name == "Animation") continue; // nope int idx = menu->get_item_count(); - menu->add_item(vformat("Add %s", name)); + menu->add_item(vformat("Add %s", name), idx); menu->set_item_metadata(idx, E->get()); } + Ref<AnimationNode> clipb = EditorSettings::get_singleton()->get_resource_clipboard(); + + if (clipb.is_valid()) { + menu->add_separator(); + menu->add_item(TTR("Paste"), MENU_PASTE); + } + menu->add_separator(); + menu->add_item(TTR("Load.."), MENU_LOAD_FILE); menu->set_global_position(state_machine_draw->get_global_transform().xform(mb->get_position())); menu->popup(); @@ -98,18 +105,12 @@ void AnimationNodeStateMachineEditor::_state_machine_gui_input(const Ref<InputEv for (int i = node_rects.size() - 1; i >= 0; i--) { //inverse to draw order if (node_rects[i].play.has_point(mb->get_position())) { //edit name - if (play_mode->get_selected() == 1 || !state_machine->is_playing()) { + if (play_mode->get_selected() == 1 || !playback->is_playing()) { //start - state_machine->start(node_rects[i].node_name); + playback->start(node_rects[i].node_name); } else { //travel - if (!state_machine->travel(node_rects[i].node_name)) { - - state_machine->start(node_rects[i].node_name); - //removing this due to usability.. - //error_time = 5; - //error_text = vformat(TTR("No path found from '%s' to '%s'."), state_machine->get_current_node(), node_rects[i].node_name); - } + playback->travel(node_rects[i].node_name); } state_machine_draw->update(); return; @@ -196,8 +197,8 @@ void AnimationNodeStateMachineEditor::_state_machine_gui_input(const Ref<InputEv Ref<AnimationNode> an = state_machine->get_node(selected_node); updating = true; undo_redo->create_action("Move Node"); - undo_redo->add_do_method(an.ptr(), "set_position", an->get_position() + drag_ofs / EDSCALE); - undo_redo->add_undo_method(an.ptr(), "set_position", an->get_position()); + undo_redo->add_do_method(state_machine.ptr(), "set_node_position", selected_node, state_machine->get_node_position(selected_node) + drag_ofs / EDSCALE); + undo_redo->add_undo_method(state_machine.ptr(), "set_node_position", selected_node, state_machine->get_node_position(selected_node)); undo_redo->add_do_method(this, "_update_graph"); undo_redo->add_undo_method(this, "_update_graph"); undo_redo->commit_action(); @@ -293,7 +294,7 @@ void AnimationNodeStateMachineEditor::_state_machine_gui_input(const Ref<InputEv snap_y = StringName(); { //snap - Vector2 cpos = state_machine->get_node(selected_node)->get_position() + drag_ofs / EDSCALE; + Vector2 cpos = state_machine->get_node_position(selected_node) + drag_ofs / EDSCALE; List<StringName> nodes; state_machine->get_node_list(&nodes); @@ -303,7 +304,7 @@ void AnimationNodeStateMachineEditor::_state_machine_gui_input(const Ref<InputEv for (List<StringName>::Element *E = nodes.front(); E; E = E->next()) { if (E->get() == selected_node) continue; - Vector2 npos = state_machine->get_node(E->get())->get_position(); + Vector2 npos = state_machine->get_node_position(E->get()); float d_x = ABS(npos.x - cpos.x); if (d_x < MIN(5, best_d_x)) { @@ -372,19 +373,58 @@ void AnimationNodeStateMachineEditor::_state_machine_gui_input(const Ref<InputEv } } +void AnimationNodeStateMachineEditor::_file_opened(const String &p_file) { + + file_loaded = ResourceLoader::load(p_file); + if (file_loaded.is_valid()) { + _add_menu_type(MENU_LOAD_FILE_CONFIRM); + } +} + void AnimationNodeStateMachineEditor::_add_menu_type(int p_index) { - String type = menu->get_item_metadata(p_index); + String base_name; + Ref<AnimationRootNode> node; - Object *obj = ClassDB::instance(type); - ERR_FAIL_COND(!obj); - AnimationNode *an = Object::cast_to<AnimationNode>(obj); - ERR_FAIL_COND(!an); + if (p_index == MENU_LOAD_FILE) { - Ref<AnimationNode> node(an); - node->set_position(add_node_pos); + open_file->clear_filters(); + List<String> filters; + ResourceLoader::get_recognized_extensions_for_type("AnimationRootNode", &filters); + for (List<String>::Element *E = filters.front(); E; E = E->next()) { + open_file->add_filter("*." + E->get()); + } + open_file->popup_centered_ratio(); + return; + } else if (p_index == MENU_LOAD_FILE_CONFIRM) { + node = file_loaded; + file_loaded.unref(); + } else if (p_index == MENU_PASTE) { + + node = EditorSettings::get_singleton()->get_resource_clipboard(); + + } else { + String type = menu->get_item_metadata(p_index); + + Object *obj = ClassDB::instance(type); + ERR_FAIL_COND(!obj); + AnimationNode *an = Object::cast_to<AnimationNode>(obj); + ERR_FAIL_COND(!an); + + node = Ref<AnimationNode>(an); + base_name = type.replace_first("AnimationNode", ""); + } + + if (!node.is_valid()) { + EditorNode::get_singleton()->show_warning(TTR("This type of node can't be used. Only root nodes are allowed.")); + return; + } + + if (base_name == String()) { + + base_name = node->get_class().replace_first("AnimationNode", ""); + } - String base_name = type.replace_first("AnimationNode", ""); int base = 1; String name = base_name; while (state_machine->has_node(name)) { @@ -394,7 +434,7 @@ void AnimationNodeStateMachineEditor::_add_menu_type(int p_index) { updating = true; undo_redo->create_action("Add Node"); - undo_redo->add_do_method(state_machine.ptr(), "add_node", name, node); + undo_redo->add_do_method(state_machine.ptr(), "add_node", name, node, add_node_pos); undo_redo->add_undo_method(state_machine.ptr(), "remove_node", name); undo_redo->add_do_method(this, "_update_graph"); undo_redo->add_undo_method(this, "_update_graph"); @@ -419,11 +459,9 @@ void AnimationNodeStateMachineEditor::_add_animation_type(int p_index) { name = base_name + " " + itos(base); } - anim->set_position(add_node_pos); - updating = true; undo_redo->create_action("Add Node"); - undo_redo->add_do_method(state_machine.ptr(), "add_node", name, anim); + undo_redo->add_do_method(state_machine.ptr(), "add_node", name, anim, add_node_pos); undo_redo->add_undo_method(state_machine.ptr(), "remove_node", name); undo_redo->add_do_method(this, "_update_graph"); undo_redo->add_undo_method(this, "_update_graph"); @@ -502,6 +540,8 @@ void AnimationNodeStateMachineEditor::_clip_dst_line_to_rect(Vector2 &r_from, Ve void AnimationNodeStateMachineEditor::_state_machine_draw() { + Ref<AnimationNodeStateMachinePlayback> playback = AnimationTreeEditor::get_singleton()->get_tree()->get(AnimationTreeEditor::get_singleton()->get_base_path() + "playback"); + Ref<StyleBox> style = get_stylebox("frame", "GraphNode"); Ref<StyleBox> style_selected = get_stylebox("selectedframe", "GraphNode"); @@ -515,10 +555,17 @@ void AnimationNodeStateMachineEditor::_state_machine_draw() { linecolor.a *= 0.3; Ref<StyleBox> playing_overlay = get_stylebox("position", "GraphNode"); - bool playing = state_machine->is_playing(); - StringName current = state_machine->get_current_node(); - StringName blend_from = state_machine->get_blend_from_node(); - Vector<StringName> travel_path = state_machine->get_travel_path(); + bool playing = false; + StringName current; + StringName blend_from; + Vector<StringName> travel_path; + + if (playback.is_valid()) { + playing = playback->is_playing(); + current = playback->get_current_node(); + blend_from = playback->get_blend_from_node(); + travel_path = playback->get_travel_path(); + } if (state_machine_draw->has_focus()) { state_machine_draw->draw_rect(Rect2(Point2(), state_machine_draw->get_size()), accent, false); @@ -534,13 +581,13 @@ void AnimationNodeStateMachineEditor::_state_machine_draw() { //snap lines if (dragging_selected) { - Vector2 from = (state_machine->get_node(selected_node)->get_position() * EDSCALE) + drag_ofs - state_machine->get_graph_offset() * EDSCALE; + Vector2 from = (state_machine->get_node_position(selected_node) * EDSCALE) + drag_ofs - state_machine->get_graph_offset() * EDSCALE; if (snap_x != StringName()) { - Vector2 to = (state_machine->get_node(snap_x)->get_position() * EDSCALE) - state_machine->get_graph_offset() * EDSCALE; + Vector2 to = (state_machine->get_node_position(snap_x) * EDSCALE) - state_machine->get_graph_offset() * EDSCALE; state_machine_draw->draw_line(from, to, linecolor, 2); } if (snap_y != StringName()) { - Vector2 to = (state_machine->get_node(snap_y)->get_position() * EDSCALE) - state_machine->get_graph_offset() * EDSCALE; + Vector2 to = (state_machine->get_node_position(snap_y) * EDSCALE) - state_machine->get_graph_offset() * EDSCALE; state_machine_draw->draw_line(from, to, linecolor, 2); } } @@ -563,7 +610,7 @@ void AnimationNodeStateMachineEditor::_state_machine_draw() { } Vector2 offset; - offset += anode->get_position() * EDSCALE; + offset += state_machine->get_node_position(E->get()) * EDSCALE; if (selected_node == E->get() && dragging_selected) { offset += drag_ofs; } @@ -588,10 +635,10 @@ void AnimationNodeStateMachineEditor::_state_machine_draw() { //draw conecting line for potential new transition if (connecting) { - Vector2 from = (state_machine->get_node(connecting_from)->get_position() * EDSCALE) - state_machine->get_graph_offset() * EDSCALE; + Vector2 from = (state_machine->get_node_position(connecting_from) * EDSCALE) - state_machine->get_graph_offset() * EDSCALE; Vector2 to; if (connecting_to_node != StringName()) { - to = (state_machine->get_node(connecting_to_node)->get_position() * EDSCALE) - state_machine->get_graph_offset() * EDSCALE; + to = (state_machine->get_node_position(connecting_to_node) * EDSCALE) - state_machine->get_graph_offset() * EDSCALE; } else { to = connecting_to; } @@ -617,15 +664,17 @@ void AnimationNodeStateMachineEditor::_state_machine_draw() { TransitionLine tl; tl.from_node = state_machine->get_transition_from(i); Vector2 ofs_from = (dragging_selected && tl.from_node == selected_node) ? drag_ofs : Vector2(); - tl.from = (state_machine->get_node(tl.from_node)->get_position() * EDSCALE) + ofs_from - state_machine->get_graph_offset() * EDSCALE; + tl.from = (state_machine->get_node_position(tl.from_node) * EDSCALE) + ofs_from - state_machine->get_graph_offset() * EDSCALE; tl.to_node = state_machine->get_transition_to(i); Vector2 ofs_to = (dragging_selected && tl.to_node == selected_node) ? drag_ofs : Vector2(); - tl.to = (state_machine->get_node(tl.to_node)->get_position() * EDSCALE) + ofs_to - state_machine->get_graph_offset() * EDSCALE; + tl.to = (state_machine->get_node_position(tl.to_node) * EDSCALE) + ofs_to - state_machine->get_graph_offset() * EDSCALE; Ref<AnimationNodeStateMachineTransition> tr = state_machine->get_transition(i); tl.disabled = tr->is_disabled(); tl.auto_advance = tr->has_auto_advance(); + tl.advance_condition_name = tr->get_advance_condition_name(); + tl.advance_condition_state = false; tl.mode = tr->get_switch_mode(); tl.width = tr_bidi_offset; @@ -665,7 +714,14 @@ void AnimationNodeStateMachineEditor::_state_machine_draw() { } } } - _connection_draw(tl.from, tl.to, tl.mode, !tl.disabled, selected, travel, tl.auto_advance); + + bool auto_advance = tl.auto_advance; + StringName fullpath = AnimationTreeEditor::get_singleton()->get_base_path() + String(tl.advance_condition_name); + if (tl.advance_condition_name != StringName() && bool(AnimationTreeEditor::get_singleton()->get_tree()->get(fullpath))) { + tl.advance_condition_state = true; + auto_advance = true; + } + _connection_draw(tl.from, tl.to, tl.mode, !tl.disabled, selected, travel, auto_advance); transition_lines.push_back(tl); } @@ -675,7 +731,7 @@ void AnimationNodeStateMachineEditor::_state_machine_draw() { String name = node_rects[i].node_name; Ref<AnimationNode> anode = state_machine->get_node(name); - bool needs_editor = EditorNode::get_singleton()->item_has_editor(anode.ptr()); + bool needs_editor = AnimationTreeEditor::get_singleton()->can_edit(anode); Ref<StyleBox> sb = name == selected_node ? style_selected : style; int strsize = font->get_string_size(name).width; @@ -757,12 +813,14 @@ void AnimationNodeStateMachineEditor::_state_machine_draw() { void AnimationNodeStateMachineEditor::_state_machine_pos_draw() { - if (!state_machine->is_playing()) + Ref<AnimationNodeStateMachinePlayback> playback = AnimationTreeEditor::get_singleton()->get_tree()->get(AnimationTreeEditor::get_singleton()->get_base_path() + "playback"); + + if (!playback.is_valid() || !playback->is_playing()) return; int idx = -1; for (int i = 0; node_rects.size(); i++) { - if (node_rects[i].node_name == state_machine->get_current_node()) { + if (node_rects[i].node_name == playback->get_current_node()) { idx = i; break; } @@ -785,9 +843,9 @@ void AnimationNodeStateMachineEditor::_state_machine_pos_draw() { } to.y = from.y; - float len = MAX(0.0001, state_machine->get_current_length()); + float len = MAX(0.0001, playback->get_current_length()); - float pos = CLAMP(state_machine->get_current_play_pos(), 0, len); + float pos = CLAMP(playback->get_current_play_pos(), 0, len); float c = pos / len; Color fg = get_color("font_color", "Label"); Color bg = fg; @@ -807,12 +865,6 @@ void AnimationNodeStateMachineEditor::_update_graph() { updating = true; - if (state_machine->get_parent().is_valid()) { - goto_parent_hbox->show(); - } else { - goto_parent_hbox->hide(); - } - state_machine_draw->update(); updating = false; @@ -824,7 +876,6 @@ void AnimationNodeStateMachineEditor::_notification(int p_what) { error_panel->add_style_override("panel", get_stylebox("bg", "Tree")); error_label->add_color_override("font_color", get_color("error_color", "Editor")); panel->add_style_override("panel", get_stylebox("bg", "Tree")); - goto_parent->set_icon(get_icon("MoveUp", "EditorIcons")); tool_select->set_icon(get_icon("ToolSelect", "EditorIcons")); tool_create->set_icon(get_icon("ToolAddNode", "EditorIcons")); @@ -856,19 +907,21 @@ void AnimationNodeStateMachineEditor::_notification(int p_what) { String error; + Ref<AnimationNodeStateMachinePlayback> playback = AnimationTreeEditor::get_singleton()->get_tree()->get(AnimationTreeEditor::get_singleton()->get_base_path() + "playback"); + if (error_time > 0) { error = error_text; error_time -= get_process_delta_time(); - } else if (!state_machine->get_tree()) { - error = TTR("StateMachine does not belong to an AnimationTree node."); - } else if (!state_machine->get_tree()->is_active()) { + } else if (!AnimationTreeEditor::get_singleton()->get_tree()->is_active()) { error = TTR("AnimationTree is inactive.\nActivate to enable playback, check node warnings if activation fails."); - } else if (state_machine->get_tree()->is_state_invalid()) { - error = state_machine->get_tree()->get_invalid_state_reason(); - } else if (state_machine->get_parent().is_valid() && state_machine->get_parent()->is_class("AnimationNodeStateMachine")) { + } else if (AnimationTreeEditor::get_singleton()->get_tree()->is_state_invalid()) { + error = AnimationTreeEditor::get_singleton()->get_tree()->get_invalid_state_reason(); + /*} else if (state_machine->get_parent().is_valid() && state_machine->get_parent()->is_class("AnimationNodeStateMachine")) { if (state_machine->get_start_node() == StringName() || state_machine->get_end_node() == StringName()) { error = TTR("Start and end nodes are needed for a sub-transition."); - } + }*/ + } else if (playback.is_null()) { + error = vformat(TTR("No playback resource set at path: %s."), AnimationTreeEditor::get_singleton()->get_base_path() + "playback"); } if (error != error_label->get_text()) { @@ -904,14 +957,38 @@ void AnimationNodeStateMachineEditor::_notification(int p_what) { break; } + if (transition_lines[i].advance_condition_name != state_machine->get_transition(tidx)->get_advance_condition_name()) { + state_machine_draw->update(); + break; + } + if (transition_lines[i].mode != state_machine->get_transition(tidx)->get_switch_mode()) { state_machine_draw->update(); break; } + + bool acstate = transition_lines[i].advance_condition_name != StringName() && bool(AnimationTreeEditor::get_singleton()->get_tree()->get(AnimationTreeEditor::get_singleton()->get_base_path() + String(transition_lines[i].advance_condition_name))); + + if (transition_lines[i].advance_condition_state != acstate) { + state_machine_draw->update(); + break; + } } bool same_travel_path = true; - Vector<StringName> tp = state_machine->get_travel_path(); + Vector<StringName> tp; + bool is_playing = false; + StringName current_node; + StringName blend_from_node; + float play_pos = 0; + + if (playback.is_valid()) { + tp = playback->get_travel_path(); + is_playing = playback->is_playing(); + current_node = playback->get_current_node(); + blend_from_node = playback->get_blend_from_node(); + play_pos = playback->get_current_play_pos(); + } { @@ -928,37 +1005,32 @@ void AnimationNodeStateMachineEditor::_notification(int p_what) { } //update if travel state changed - if (!same_travel_path || last_active != state_machine->is_playing() || last_current_node != state_machine->get_current_node() || last_blend_from_node != state_machine->get_blend_from_node()) { + if (!same_travel_path || last_active != is_playing || last_current_node != current_node || last_blend_from_node != blend_from_node) { state_machine_draw->update(); last_travel_path = tp; - last_current_node = state_machine->get_current_node(); - last_active = state_machine->is_playing(); - last_blend_from_node = state_machine->get_blend_from_node(); + last_current_node = current_node; + last_active = is_playing; + last_blend_from_node = blend_from_node; state_machine_play_pos->update(); } - if (last_play_pos != state_machine->get_current_play_pos()) { + if (last_play_pos != play_pos) { - last_play_pos = state_machine->get_current_play_pos(); + last_play_pos = play_pos; state_machine_play_pos->update(); } } if (p_what == NOTIFICATION_VISIBILITY_CHANGED) { over_node = StringName(); + set_process(is_visible_in_tree()); } } void AnimationNodeStateMachineEditor::_open_editor(const String &p_name) { - Ref<AnimationNode> an = state_machine->get_node(p_name); - ERR_FAIL_COND(!an.is_valid()); - EditorNode::get_singleton()->edit_item(an.ptr()); -} - -void AnimationNodeStateMachineEditor::_goto_parent() { - EditorNode::get_singleton()->edit_item(state_machine->get_parent().ptr()); + AnimationTreeEditor::get_singleton()->enter_editor(p_name); } void AnimationNodeStateMachineEditor::_removed_from_graph() { @@ -1114,7 +1186,6 @@ void AnimationNodeStateMachineEditor::_bind_methods() { ClassDB::bind_method("_name_edited", &AnimationNodeStateMachineEditor::_name_edited); - ClassDB::bind_method("_goto_parent", &AnimationNodeStateMachineEditor::_goto_parent); ClassDB::bind_method("_removed_from_graph", &AnimationNodeStateMachineEditor::_removed_from_graph); ClassDB::bind_method("_open_editor", &AnimationNodeStateMachineEditor::_open_editor); @@ -1124,6 +1195,7 @@ void AnimationNodeStateMachineEditor::_bind_methods() { ClassDB::bind_method("_autoplay_selected", &AnimationNodeStateMachineEditor::_autoplay_selected); ClassDB::bind_method("_end_selected", &AnimationNodeStateMachineEditor::_end_selected); ClassDB::bind_method("_update_mode", &AnimationNodeStateMachineEditor::_update_mode); + ClassDB::bind_method("_file_opened", &AnimationNodeStateMachineEditor::_file_opened); } AnimationNodeStateMachineEditor *AnimationNodeStateMachineEditor::singleton = NULL; @@ -1136,13 +1208,6 @@ AnimationNodeStateMachineEditor::AnimationNodeStateMachineEditor() { HBoxContainer *top_hb = memnew(HBoxContainer); add_child(top_hb); - goto_parent_hbox = memnew(HBoxContainer); - goto_parent = memnew(ToolButton); - goto_parent->connect("pressed", this, "_goto_parent", varray(), CONNECT_DEFERRED); - goto_parent_hbox->add_child(goto_parent); - goto_parent_hbox->add_child(memnew(VSeparator)); - top_hb->add_child(goto_parent_hbox); - Ref<ButtonGroup> bg; bg.instance(); @@ -1248,7 +1313,7 @@ AnimationNodeStateMachineEditor::AnimationNodeStateMachineEditor() { menu = memnew(PopupMenu); add_child(menu); - menu->connect("index_pressed", this, "_add_menu_type"); + menu->connect("id_pressed", this, "_add_menu_type"); animations_menu = memnew(PopupMenu); menu->add_child(animations_menu); @@ -1261,6 +1326,13 @@ AnimationNodeStateMachineEditor::AnimationNodeStateMachineEditor() { name_edit->connect("text_entered", this, "_name_edited"); name_edit->set_as_toplevel(true); + open_file = memnew(EditorFileDialog); + add_child(open_file); + open_file->set_title(TTR("Open Animation Node")); + open_file->set_mode(EditorFileDialog::MODE_OPEN_FILE); + open_file->connect("file_selected", this, "_file_opened"); + undo_redo = EditorNode::get_singleton()->get_undo_redo(); + over_text = false; over_node_what = -1; @@ -1271,43 +1343,3 @@ AnimationNodeStateMachineEditor::AnimationNodeStateMachineEditor() { error_time = 0; } - -void AnimationNodeStateMachineEditorPlugin::edit(Object *p_object) { - - anim_tree_editor->edit(Object::cast_to<AnimationNodeStateMachine>(p_object)); -} - -bool AnimationNodeStateMachineEditorPlugin::handles(Object *p_object) const { - - return p_object->is_class("AnimationNodeStateMachine"); -} - -void AnimationNodeStateMachineEditorPlugin::make_visible(bool p_visible) { - - if (p_visible) { - //editor->hide_animation_player_editors(); - //editor->animation_panel_make_visible(true); - button->show(); - editor->make_bottom_panel_item_visible(anim_tree_editor); - anim_tree_editor->set_process(true); - } else { - - if (anim_tree_editor->is_visible_in_tree()) - editor->hide_bottom_panel(); - button->hide(); - anim_tree_editor->set_process(false); - } -} - -AnimationNodeStateMachineEditorPlugin::AnimationNodeStateMachineEditorPlugin(EditorNode *p_node) { - - editor = p_node; - anim_tree_editor = memnew(AnimationNodeStateMachineEditor); - anim_tree_editor->set_custom_minimum_size(Size2(0, 300)); - - button = editor->add_bottom_panel_item(TTR("StateMachine"), anim_tree_editor); - button->hide(); -} - -AnimationNodeStateMachineEditorPlugin::~AnimationNodeStateMachineEditorPlugin() { -} diff --git a/editor/plugins/animation_state_machine_editor.h b/editor/plugins/animation_state_machine_editor.h index efd3de7415..49d08607cf 100644 --- a/editor/plugins/animation_state_machine_editor.h +++ b/editor/plugins/animation_state_machine_editor.h @@ -3,6 +3,7 @@ #include "editor/editor_node.h" #include "editor/editor_plugin.h" +#include "editor/plugins/animation_tree_editor_plugin.h" #include "editor/property_editor.h" #include "scene/animation/animation_node_state_machine.h" #include "scene/gui/button.h" @@ -10,9 +11,9 @@ #include "scene/gui/popup.h" #include "scene/gui/tree.h" -class AnimationNodeStateMachineEditor : public VBoxContainer { +class AnimationNodeStateMachineEditor : public AnimationTreeNodeEditorPlugin { - GDCLASS(AnimationNodeStateMachineEditor, VBoxContainer); + GDCLASS(AnimationNodeStateMachineEditor, AnimationTreeNodeEditorPlugin); Ref<AnimationNodeStateMachine> state_machine; @@ -29,9 +30,6 @@ class AnimationNodeStateMachineEditor : public VBoxContainer { OptionButton *transition_mode; OptionButton *play_mode; - HBoxContainer *goto_parent_hbox; - ToolButton *goto_parent; - PanelContainer *panel; StringName selected_node; @@ -79,8 +77,6 @@ class AnimationNodeStateMachineEditor : public VBoxContainer { void _add_menu_type(int p_index); void _add_animation_type(int p_index); - void _goto_parent(); - void _removed_from_graph(); struct NodeRect { @@ -99,6 +95,8 @@ class AnimationNodeStateMachineEditor : public VBoxContainer { Vector2 from; Vector2 to; AnimationNodeStateMachineTransition::SwitchMode mode; + StringName advance_condition_name; + bool advance_condition_state; bool disabled; bool auto_advance; float width; @@ -135,33 +133,25 @@ class AnimationNodeStateMachineEditor : public VBoxContainer { float error_time; String error_text; + EditorFileDialog *open_file; + Ref<AnimationNode> file_loaded; + void _file_opened(const String &p_file); + + enum { + MENU_LOAD_FILE = 1000, + MENU_PASTE = 1001, + MENU_LOAD_FILE_CONFIRM = 1002 + }; + protected: void _notification(int p_what); static void _bind_methods(); public: static AnimationNodeStateMachineEditor *get_singleton() { return singleton; } - void edit(AnimationNodeStateMachine *p_state_machine); + virtual bool can_edit(const Ref<AnimationNode> &p_node); + virtual void edit(const Ref<AnimationNode> &p_node); AnimationNodeStateMachineEditor(); }; -class AnimationNodeStateMachineEditorPlugin : public EditorPlugin { - - GDCLASS(AnimationNodeStateMachineEditorPlugin, EditorPlugin); - - AnimationNodeStateMachineEditor *anim_tree_editor; - EditorNode *editor; - Button *button; - -public: - virtual String get_name() const { return "StateMachine"; } - bool has_main_screen() const { return false; } - virtual void edit(Object *p_object); - virtual bool handles(Object *p_object) const; - virtual void make_visible(bool p_visible); - - AnimationNodeStateMachineEditorPlugin(EditorNode *p_node); - ~AnimationNodeStateMachineEditorPlugin(); -}; - #endif // ANIMATION_STATE_MACHINE_EDITOR_H diff --git a/editor/plugins/animation_tree_editor_plugin.cpp b/editor/plugins/animation_tree_editor_plugin.cpp index 25582ae0b9..19921ef54f 100644 --- a/editor/plugins/animation_tree_editor_plugin.cpp +++ b/editor/plugins/animation_tree_editor_plugin.cpp @@ -1,1418 +1,247 @@ -/*************************************************************************/ -/* animation_tree_editor_plugin.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - #include "animation_tree_editor_plugin.h" +#include "animation_blend_space_1d_editor.h" +#include "animation_blend_space_2d_editor.h" +#include "animation_blend_tree_editor_plugin.h" +#include "animation_state_machine_editor.h" #include "core/io/resource_loader.h" #include "core/project_settings.h" +#include "math/delaunay.h" #include "os/input.h" #include "os/keyboard.h" +#include "scene/animation/animation_blend_tree.h" +#include "scene/animation/animation_player.h" #include "scene/gui/menu_button.h" #include "scene/gui/panel.h" #include "scene/main/viewport.h" +#include "scene/scene_string_names.h" -void AnimationTreeEditor::edit(AnimationTreePlayer *p_anim_tree) { +void AnimationTreeEditor::edit(AnimationTree *p_tree) { - anim_tree = p_anim_tree; + if (tree == p_tree) + return; - if (!anim_tree) { - hide(); - } else { - order.clear(); - p_anim_tree->get_node_list(&order); - /* - for(List<StringName>::Element* E=order.front();E;E=E->next()) { + tree = p_tree; - if (E->get() >= (int)last_id) - last_id=E->get()+1; - }*/ - play_button->set_pressed(p_anim_tree->is_active()); - //read the orders + Vector<String> path; + if (tree->has_meta("_tree_edit_path")) { + path = tree->get_meta("_tree_edit_path"); + edit_path(path); + } else { + current_root = 0; } } -Size2 AnimationTreeEditor::_get_maximum_size() { - - Size2 max; - - for (List<StringName>::Element *E = order.front(); E; E = E->next()) { +void AnimationTreeEditor::_path_button_pressed(int p_path) { - Point2 pos = anim_tree->node_get_position(E->get()); - - if (click_type == CLICK_NODE && click_node == E->get()) { + Ref<AnimationNode> node = tree->get_tree_root(); + if (node.is_null()) + return; - pos += click_motion - click_pos; + edited_path.clear(); + if (p_path >= 0) { + for (int i = 0; i <= p_path; i++) { + Ref<AnimationNode> child = node->get_child_by_name(button_path[i]); + ERR_BREAK(child.is_null()); + node = child; + edited_path.push_back(button_path[i]); } - pos += get_node_size(E->get()); - if (pos.x > max.x) - max.x = pos.x; - if (pos.y > max.y) - max.y = pos.y; } - return max; -} - -const char *AnimationTreeEditor::_node_type_names[] = { "Output", "Animation", "OneShot", "Mix", "Blend2", "Blend3", "Blend4", "TimeScale", "TimeSeek", "Transition" }; - -Size2 AnimationTreeEditor::get_node_size(const StringName &p_node) const { - - AnimationTreePlayer::NodeType type = anim_tree->node_get_type(p_node); - - Ref<StyleBox> style = get_stylebox("panel", "PopupMenu"); - Ref<Font> font = get_font("font", "PopupMenu"); - - Size2 size = style->get_minimum_size(); - - int count = 2; // title and name - int inputs = anim_tree->node_get_input_count(p_node); - count += inputs ? inputs : 1; - String name = p_node; - - float name_w = font->get_string_size(name).width; - float type_w = font->get_string_size(String(_node_type_names[type])).width; - float max_w = MAX(name_w, type_w); - - switch (type) { - case AnimationTreePlayer::NODE_TIMESEEK: - case AnimationTreePlayer::NODE_OUTPUT: { - } break; - case AnimationTreePlayer::NODE_ANIMATION: - case AnimationTreePlayer::NODE_ONESHOT: - case AnimationTreePlayer::NODE_MIX: - case AnimationTreePlayer::NODE_BLEND2: - case AnimationTreePlayer::NODE_BLEND3: - case AnimationTreePlayer::NODE_BLEND4: - case AnimationTreePlayer::NODE_TIMESCALE: - case AnimationTreePlayer::NODE_TRANSITION: { - - size.height += font->get_height(); - } break; - case AnimationTreePlayer::NODE_MAX: { + for (int i = 0; i < editors.size(); i++) { + if (editors[i]->can_edit(node)) { + editors[i]->edit(node); + editors[i]->show(); + } else { + editors[i]->edit(Ref<AnimationNode>()); + editors[i]->hide(); } } - - size.x += max_w + 20; - size.y += count * (font->get_height() + get_constant("vseparation", "PopupMenu")); - - return size; } -void AnimationTreeEditor::_edit_dialog_changede(String) { - - edit_dialog->hide(); -} - -void AnimationTreeEditor::_edit_dialog_changeds(String s) { - - _edit_dialog_changed(); -} - -void AnimationTreeEditor::_edit_dialog_changedf(float) { - - _edit_dialog_changed(); -} - -void AnimationTreeEditor::_edit_dialog_changed() { - - if (updating_edit) - return; - - if (renaming_edit) { - - if (anim_tree->node_rename(edited_node, edit_line[0]->get_text()) == OK) { - for (List<StringName>::Element *E = order.front(); E; E = E->next()) { - - if (E->get() == edited_node) - E->get() = edit_line[0]->get_text(); - } - edited_node = edit_line[0]->get_text(); - } - update(); - return; +void AnimationTreeEditor::_update_path() { + while (path_hb->get_child_count()) { + memdelete(path_hb->get_child(0)); } - AnimationTreePlayer::NodeType type = anim_tree->node_get_type(edited_node); - - switch (type) { - - case AnimationTreePlayer::NODE_TIMESCALE: - anim_tree->timescale_node_set_scale(edited_node, edit_line[0]->get_text().to_double()); - break; - case AnimationTreePlayer::NODE_ONESHOT: - anim_tree->oneshot_node_set_fadein_time(edited_node, edit_line[0]->get_text().to_double()); - anim_tree->oneshot_node_set_fadeout_time(edited_node, edit_line[1]->get_text().to_double()); - anim_tree->oneshot_node_set_autorestart_delay(edited_node, edit_line[2]->get_text().to_double()); - anim_tree->oneshot_node_set_autorestart_random_delay(edited_node, edit_line[3]->get_text().to_double()); - anim_tree->oneshot_node_set_autorestart(edited_node, edit_check->is_pressed()); - anim_tree->oneshot_node_set_mix_mode(edited_node, edit_option->get_selected()); - - break; - - case AnimationTreePlayer::NODE_MIX: - - anim_tree->mix_node_set_amount(edited_node, edit_scroll[0]->get_value()); - break; - case AnimationTreePlayer::NODE_BLEND2: - anim_tree->blend2_node_set_amount(edited_node, edit_scroll[0]->get_value()); - - break; - - case AnimationTreePlayer::NODE_BLEND3: - anim_tree->blend3_node_set_amount(edited_node, edit_scroll[0]->get_value()); - - break; - case AnimationTreePlayer::NODE_BLEND4: - - anim_tree->blend4_node_set_amount(edited_node, Point2(edit_scroll[0]->get_value(), edit_scroll[1]->get_value())); - - break; - - case AnimationTreePlayer::NODE_TRANSITION: { - anim_tree->transition_node_set_xfade_time(edited_node, edit_line[0]->get_text().to_double()); - if (anim_tree->transition_node_get_current(edited_node) != edit_option->get_selected()) - anim_tree->transition_node_set_current(edited_node, edit_option->get_selected()); - } break; - default: {} - } -} - -void AnimationTreeEditor::_edit_dialog_animation_changed() { - - Ref<Animation> anim = property_editor->get_variant().operator RefPtr(); - anim_tree->animation_node_set_animation(edited_node, anim); - update(); -} - -void AnimationTreeEditor::_edit_dialog_edit_animation() { - - if (Engine::get_singleton()->is_editor_hint()) { - get_tree()->get_root()->get_child(0)->call("_resource_selected", property_editor->get_variant().operator RefPtr()); - }; -}; - -void AnimationTreeEditor::_edit_oneshot_start() { - - anim_tree->oneshot_node_start(edited_node); -} - -void AnimationTreeEditor::_play_toggled() { - - anim_tree->set_active(play_button->is_pressed()); -} - -void AnimationTreeEditor::_master_anim_menu_item(int p_item) { - - if (p_item == 0) - _edit_filters(); - else { - - String str = master_anim_popup->get_item_text(p_item); - anim_tree->animation_node_set_master_animation(edited_node, str); + Ref<ButtonGroup> group; + group.instance(); + + Button *b = memnew(Button); + b->set_text("root"); + b->set_toggle_mode(true); + b->set_button_group(group); + b->set_pressed(true); + b->set_focus_mode(FOCUS_NONE); + b->connect("pressed", this, "_path_button_pressed", varray(-1)); + path_hb->add_child(b); + for (int i = 0; i < button_path.size(); i++) { + b = memnew(Button); + b->set_text(button_path[i]); + b->set_toggle_mode(true); + b->set_button_group(group); + path_hb->add_child(b); + b->set_pressed(true); + b->set_focus_mode(FOCUS_NONE); + b->connect("pressed", this, "_path_button_pressed", varray(i)); } - update(); } -void AnimationTreeEditor::_popup_edit_dialog() { - - updating_edit = true; - - for (int i = 0; i < 2; i++) - edit_scroll[i]->hide(); - - for (int i = 0; i < 4; i++) { - - edit_line[i]->hide(); - edit_label[i]->hide(); - } - - edit_option->hide(); - edit_button->hide(); - filter_button->hide(); - edit_check->hide(); - - Point2 pos = anim_tree->node_get_position(edited_node) - Point2(h_scroll->get_value(), v_scroll->get_value()); - Ref<StyleBox> style = get_stylebox("panel", "PopupMenu"); - Size2 size = get_node_size(edited_node); - Point2 popup_pos(pos.x + style->get_margin(MARGIN_LEFT), pos.y + size.y - style->get_margin(MARGIN_BOTTOM)); - popup_pos += get_global_position(); - - if (renaming_edit) { - - edit_label[0]->set_text(TTR("New name:")); - edit_label[0]->set_position(Point2(5, 5)); - edit_label[0]->show(); - edit_line[0]->set_begin(Point2(15, 25)); - edit_line[0]->set_text(edited_node); - edit_line[0]->show(); - edit_dialog->set_size(Size2(150, 50)); - - } else { - - AnimationTreePlayer::NodeType type = anim_tree->node_get_type(edited_node); - - switch (type) { - - case AnimationTreePlayer::NODE_ANIMATION: - - if (anim_tree->get_master_player() != NodePath() && anim_tree->has_node(anim_tree->get_master_player()) && Object::cast_to<AnimationPlayer>(anim_tree->get_node(anim_tree->get_master_player()))) { - - AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(anim_tree->get_node(anim_tree->get_master_player())); - master_anim_popup->clear(); - master_anim_popup->add_item(TTR("Edit Filters")); - master_anim_popup->add_separator(); - List<StringName> sn; - ap->get_animation_list(&sn); - sn.sort_custom<StringName::AlphCompare>(); - for (List<StringName>::Element *E = sn.front(); E; E = E->next()) { - master_anim_popup->add_item(E->get()); - } - - master_anim_popup->set_position(popup_pos); - master_anim_popup->popup(); - } else { - property_editor->edit(this, "", Variant::OBJECT, anim_tree->animation_node_get_animation(edited_node), PROPERTY_HINT_RESOURCE_TYPE, "Animation"); - property_editor->set_position(popup_pos); - property_editor->popup(); - updating_edit = false; - } - return; - case AnimationTreePlayer::NODE_TIMESCALE: - edit_label[0]->set_text(TTR("Scale:")); - edit_label[0]->set_position(Point2(5, 5)); - edit_label[0]->show(); - edit_line[0]->set_begin(Point2(15, 25)); - edit_line[0]->set_text(rtos(anim_tree->timescale_node_get_scale(edited_node))); - edit_line[0]->show(); - edit_dialog->set_size(Size2(150, 50)); - break; - case AnimationTreePlayer::NODE_ONESHOT: - edit_label[0]->set_text(TTR("Fade In (s):")); - edit_label[0]->set_position(Point2(5, 5)); - edit_label[0]->show(); - edit_line[0]->set_begin(Point2(15, 25)); - edit_line[0]->set_text(rtos(anim_tree->oneshot_node_get_fadein_time(edited_node))); - edit_line[0]->show(); - edit_label[1]->set_text(TTR("Fade Out (s):")); - edit_label[1]->set_position(Point2(5, 55)); - edit_label[1]->show(); - edit_line[1]->set_begin(Point2(15, 75)); - edit_line[1]->set_text(rtos(anim_tree->oneshot_node_get_fadeout_time(edited_node))); - edit_line[1]->show(); - - edit_option->clear(); - edit_option->add_item(TTR("Blend"), 0); - edit_option->add_item(TTR("Mix"), 1); - edit_option->set_begin(Point2(15, 105)); - - edit_option->select(anim_tree->oneshot_node_get_mix_mode(edited_node)); - edit_option->show(); - - edit_check->set_text(TTR("Auto Restart:")); - edit_check->set_begin(Point2(15, 125)); - edit_check->set_pressed(anim_tree->oneshot_node_has_autorestart(edited_node)); - edit_check->show(); - - edit_label[2]->set_text(TTR("Restart (s):")); - edit_label[2]->set_position(Point2(5, 145)); - edit_label[2]->show(); - edit_line[2]->set_begin(Point2(15, 165)); - edit_line[2]->set_text(rtos(anim_tree->oneshot_node_get_autorestart_delay(edited_node))); - edit_line[2]->show(); - edit_label[3]->set_text(TTR("Random Restart (s):")); - edit_label[3]->set_position(Point2(5, 195)); - edit_label[3]->show(); - edit_line[3]->set_begin(Point2(15, 215)); - edit_line[3]->set_text(rtos(anim_tree->oneshot_node_get_autorestart_random_delay(edited_node))); - edit_line[3]->show(); - - filter_button->set_begin(Point2(10, 245)); - filter_button->show(); - - edit_button->set_begin(Point2(10, 268)); - edit_button->set_text(TTR("Start!")); - - edit_button->show(); +void AnimationTreeEditor::edit_path(const Vector<String> &p_path) { - edit_dialog->set_size(Size2(180, 293)); + button_path.clear(); - break; + Ref<AnimationNode> node = tree->get_tree_root(); - case AnimationTreePlayer::NODE_MIX: + if (node.is_valid()) { + current_root = node->get_instance_id(); - edit_label[0]->set_text(TTR("Amount:")); - edit_label[0]->set_position(Point2(5, 5)); - edit_label[0]->show(); - edit_scroll[0]->set_min(0); - edit_scroll[0]->set_max(1); - edit_scroll[0]->set_step(0.01); - edit_scroll[0]->set_value(anim_tree->mix_node_get_amount(edited_node)); - edit_scroll[0]->set_begin(Point2(15, 25)); - edit_scroll[0]->show(); - edit_dialog->set_size(Size2(150, 50)); + for (int i = 0; i < p_path.size(); i++) { - break; - case AnimationTreePlayer::NODE_BLEND2: - edit_label[0]->set_text(TTR("Blend:")); - edit_label[0]->set_position(Point2(5, 5)); - edit_label[0]->show(); - edit_scroll[0]->set_min(0); - edit_scroll[0]->set_max(1); - edit_scroll[0]->set_step(0.01); - edit_scroll[0]->set_value(anim_tree->blend2_node_get_amount(edited_node)); - edit_scroll[0]->set_begin(Point2(15, 25)); - edit_scroll[0]->show(); - filter_button->set_begin(Point2(10, 47)); - filter_button->show(); - edit_dialog->set_size(Size2(150, 74)); - - break; - - case AnimationTreePlayer::NODE_BLEND3: - edit_label[0]->set_text(TTR("Blend:")); - edit_label[0]->set_position(Point2(5, 5)); - edit_label[0]->show(); - edit_scroll[0]->set_min(-1); - edit_scroll[0]->set_max(1); - edit_scroll[0]->set_step(0.01); - edit_scroll[0]->set_value(anim_tree->blend3_node_get_amount(edited_node)); - edit_scroll[0]->set_begin(Point2(15, 25)); - edit_scroll[0]->show(); - edit_dialog->set_size(Size2(150, 50)); - - break; - case AnimationTreePlayer::NODE_BLEND4: - - edit_label[0]->set_text(TTR("Blend 0:")); - edit_label[0]->set_position(Point2(5, 5)); - edit_label[0]->show(); - edit_scroll[0]->set_min(0); - edit_scroll[0]->set_max(1); - edit_scroll[0]->set_step(0.01); - edit_scroll[0]->set_value(anim_tree->blend4_node_get_amount(edited_node).x); - edit_scroll[0]->set_begin(Point2(15, 25)); - edit_scroll[0]->show(); - edit_label[1]->set_text(TTR("Blend 1:")); - edit_label[1]->set_position(Point2(5, 55)); - edit_label[1]->show(); - edit_scroll[1]->set_min(0); - edit_scroll[1]->set_max(1); - edit_scroll[1]->set_step(0.01); - edit_scroll[1]->set_value(anim_tree->blend4_node_get_amount(edited_node).y); - edit_scroll[1]->set_begin(Point2(15, 75)); - edit_scroll[1]->show(); - edit_dialog->set_size(Size2(150, 100)); - - break; - - case AnimationTreePlayer::NODE_TRANSITION: { - - edit_label[0]->set_text(TTR("X-Fade Time (s):")); - edit_label[0]->set_position(Point2(5, 5)); - edit_label[0]->show(); - edit_line[0]->set_begin(Point2(15, 25)); - edit_line[0]->set_text(rtos(anim_tree->transition_node_get_xfade_time(edited_node))); - edit_line[0]->show(); - - edit_label[1]->set_text(TTR("Current:")); - edit_label[1]->set_position(Point2(5, 55)); - edit_label[1]->show(); - edit_option->set_begin(Point2(15, 75)); - - edit_option->clear(); - - for (int i = 0; i < anim_tree->transition_node_get_input_count(edited_node); i++) { - edit_option->add_item(itos(i), i); - } - - edit_option->select(anim_tree->transition_node_get_current(edited_node)); - edit_option->show(); - edit_dialog->set_size(Size2(150, 100)); - - } break; - default: {} + Ref<AnimationNode> child = node->get_child_by_name(p_path[i]); + ERR_BREAK(child.is_null()); + node = child; + button_path.push_back(p_path[i]); } - } - - edit_dialog->set_position(popup_pos); - edit_dialog->popup(); - - updating_edit = false; -} - -void AnimationTreeEditor::_draw_node(const StringName &p_node) { - - RID ci = get_canvas_item(); - AnimationTreePlayer::NodeType type = anim_tree->node_get_type(p_node); - - Ref<StyleBox> style = get_stylebox("panel", "PopupMenu"); - Ref<Font> font = get_font("font", "PopupMenu"); - Color font_color = get_color("font_color", "PopupMenu"); - Color font_color_title = get_color("font_color_hover", "PopupMenu"); - font_color_title.a *= 0.8; - Ref<Texture> slot_icon = get_icon("VisualShaderPort", "EditorIcons"); - - Size2 size = get_node_size(p_node); - Point2 pos = anim_tree->node_get_position(p_node); - if (click_type == CLICK_NODE && click_node == p_node) { - - pos += click_motion - click_pos; - if (pos.x < 5) - pos.x = 5; - if (pos.y < 5) - pos.y = 5; - } - - pos -= Point2(h_scroll->get_value(), v_scroll->get_value()); - style->draw(ci, Rect2(pos, size)); - - float w = size.width - style->get_minimum_size().width; - float h = font->get_height() + get_constant("vseparation", "PopupMenu"); - - Point2 ofs = style->get_offset() + pos; - Point2 ascofs(0, font->get_ascent()); - - Color bx = font_color_title; - bx.a *= 0.1; - draw_rect(Rect2(ofs, Size2(size.width - style->get_minimum_size().width, font->get_height())), bx); - font->draw_halign(ci, ofs + ascofs, HALIGN_CENTER, w, String(_node_type_names[type]), font_color_title); - - ofs.y += h; - font->draw_halign(ci, ofs + ascofs, HALIGN_CENTER, w, p_node, font_color); - ofs.y += h; - - int count = 2; // title and name - int inputs = anim_tree->node_get_input_count(p_node); - count += inputs ? inputs : 1; - - float icon_h_ofs = Math::floor((font->get_height() - slot_icon->get_height()) / 2.0) + 1; - - if (type != AnimationTreePlayer::NODE_OUTPUT) - slot_icon->draw(ci, ofs + Point2(w, icon_h_ofs)); //output - - if (inputs) { - for (int i = 0; i < inputs; i++) { - - slot_icon->draw(ci, ofs + Point2(-slot_icon->get_width(), icon_h_ofs)); - String text; - switch (type) { - - case AnimationTreePlayer::NODE_TIMESCALE: - case AnimationTreePlayer::NODE_TIMESEEK: text = "in"; break; - case AnimationTreePlayer::NODE_OUTPUT: text = "out"; break; - case AnimationTreePlayer::NODE_ANIMATION: break; - case AnimationTreePlayer::NODE_ONESHOT: text = (i == 0 ? "in" : "add"); break; - case AnimationTreePlayer::NODE_BLEND2: - case AnimationTreePlayer::NODE_MIX: text = (i == 0 ? "a" : "b"); break; - case AnimationTreePlayer::NODE_BLEND3: - switch (i) { - case 0: text = "b-"; break; - case 1: text = "a"; break; - case 2: text = "b+"; break; - } - break; - - case AnimationTreePlayer::NODE_BLEND4: - switch (i) { - case 0: text = "a0"; break; - case 1: text = "b0"; break; - case 2: text = "a1"; break; - case 3: text = "b1"; break; - } - break; - - case AnimationTreePlayer::NODE_TRANSITION: - text = itos(i); - if (anim_tree->transition_node_has_input_auto_advance(p_node, i)) - text += "->"; - - break; - default: {} + for (int i = 0; i < editors.size(); i++) { + if (editors[i]->can_edit(node)) { + editors[i]->edit(node); + editors[i]->show(); + } else { + editors[i]->edit(Ref<AnimationNode>()); + editors[i]->hide(); } - font->draw(ci, ofs + ascofs + Point2(3, 0), text, font_color); - - ofs.y += h; } } else { - ofs.y += h; + current_root = 0; } - Ref<StyleBox> pg_bg = get_stylebox("bg", "ProgressBar"); - Ref<StyleBox> pg_fill = get_stylebox("fill", "ProgressBar"); - Rect2 pg_rect(ofs, Size2(w, h)); - - bool editable = true; - switch (type) { - case AnimationTreePlayer::NODE_ANIMATION: { - - Ref<Animation> anim = anim_tree->animation_node_get_animation(p_node); - String text; - if (anim_tree->animation_node_get_master_animation(p_node) != "") - text = anim_tree->animation_node_get_master_animation(p_node); - else if (anim.is_null()) - text = "load..."; - else - text = anim->get_name(); - - font->draw_halign(ci, ofs + ascofs, HALIGN_CENTER, w, text, font_color_title); + edited_path = button_path; - } break; - case AnimationTreePlayer::NODE_ONESHOT: - case AnimationTreePlayer::NODE_MIX: - case AnimationTreePlayer::NODE_BLEND2: - case AnimationTreePlayer::NODE_BLEND3: - case AnimationTreePlayer::NODE_BLEND4: - case AnimationTreePlayer::NODE_TIMESCALE: - case AnimationTreePlayer::NODE_TRANSITION: { - - font->draw_halign(ci, ofs + ascofs, HALIGN_CENTER, w, "edit...", font_color_title); - } break; - default: editable = false; - } - - if (editable) { - - Ref<Texture> arrow = get_icon("GuiDropdown", "EditorIcons"); - Point2 arrow_ofs(w - arrow->get_width(), Math::floor((h - arrow->get_height()) / 2)); - arrow->draw(ci, ofs + arrow_ofs); - } + _update_path(); } -AnimationTreeEditor::ClickType AnimationTreeEditor::_locate_click(const Point2 &p_click, StringName *p_node_id, int *p_slot_index) const { - - Ref<StyleBox> style = get_stylebox("panel", "PopupMenu"); - Ref<Font> font = get_font("font", "PopupMenu"); - - float h = (font->get_height() + get_constant("vseparation", "PopupMenu")); - - for (const List<StringName>::Element *E = order.back(); E; E = E->prev()) { - - StringName node = E->get(); - - AnimationTreePlayer::NodeType type = anim_tree->node_get_type(node); - - Point2 pos = anim_tree->node_get_position(node); - Size2 size = get_node_size(node); - - pos -= Point2(h_scroll->get_value(), v_scroll->get_value()); - - if (!Rect2(pos, size).has_point(p_click)) - continue; - - if (p_node_id) - *p_node_id = node; - - pos = p_click - pos; - - float y = pos.y - style->get_offset().height; - - if (y < 2 * h) - return CLICK_NODE; - y -= 2 * h; - - int inputs = anim_tree->node_get_input_count(node); - int count = MAX(inputs, 1); - - if (inputs == 0 || (pos.x > size.width / 2 && type != AnimationTreePlayer::NODE_OUTPUT)) { - - if (y < count * h) { - - if (p_slot_index) - *p_slot_index = 0; - return CLICK_OUTPUT_SLOT; - } - } - - for (int i = 0; i < count; i++) { - - if (y < h) { - if (p_slot_index) - *p_slot_index = i; - return CLICK_INPUT_SLOT; - } - y -= h; - } - - bool has_parameters = type != AnimationTreePlayer::NODE_OUTPUT && type != AnimationTreePlayer::NODE_TIMESEEK; - return has_parameters ? CLICK_PARAMETER : CLICK_NODE; - } - - return CLICK_NONE; -} - -Point2 AnimationTreeEditor::_get_slot_pos(const StringName &p_node_id, bool p_input, int p_slot) { - - Ref<StyleBox> style = get_stylebox("panel", "PopupMenu"); - Ref<Font> font = get_font("font", "PopupMenu"); - Ref<Texture> slot_icon = get_icon("VisualShaderPort", "EditorIcons"); - - Size2 size = get_node_size(p_node_id); - Point2 pos = anim_tree->node_get_position(p_node_id); - - if (click_type == CLICK_NODE && click_node == p_node_id) { - - pos += click_motion - click_pos; - if (pos.x < 5) - pos.x = 5; - if (pos.y < 5) - pos.y = 5; - } - - pos -= Point2(h_scroll->get_value(), v_scroll->get_value()); - - float w = size.width - style->get_minimum_size().width; - float h = font->get_height() + get_constant("vseparation", "PopupMenu"); - - pos += style->get_offset(); - - pos.y += h * 2; - - pos.y += h * p_slot; - - pos += Point2(-slot_icon->get_width() / 2.0, h / 2.0).floor(); - - if (!p_input) { - pos.x += w + slot_icon->get_width(); - } - - return pos; +Vector<String> AnimationTreeEditor::get_edited_path() const { + return button_path; } -void AnimationTreeEditor::_gui_input(Ref<InputEvent> p_event) { - - Ref<InputEventMouseButton> mb = p_event; - - if (mb.is_valid()) { - - if (mb->is_pressed()) { +void AnimationTreeEditor::enter_editor(const String &p_path) { - if (mb->get_button_index() == 1) { - click_pos = Point2(mb->get_position().x, mb->get_position().y); - click_motion = click_pos; - click_type = _locate_click(click_pos, &click_node, &click_slot); - if (click_type != CLICK_NONE) { - - order.erase(click_node); - order.push_back(click_node); - update(); - } - - switch (click_type) { - case CLICK_INPUT_SLOT: { - click_pos = _get_slot_pos(click_node, true, click_slot); - } break; - case CLICK_OUTPUT_SLOT: { - click_pos = _get_slot_pos(click_node, false, click_slot); - } break; - case CLICK_PARAMETER: { - - edited_node = click_node; - renaming_edit = false; - _popup_edit_dialog(); - //open editor - //_node_edit_property(click_node); - } break; - default: {} - } - } - if (mb->get_button_index() == 2) { - - if (click_type != CLICK_NONE) { - click_type = CLICK_NONE; - update(); - } else { - // try to disconnect/remove - - Point2 rclick_pos = Point2(mb->get_position().x, mb->get_position().y); - rclick_type = _locate_click(rclick_pos, &rclick_node, &rclick_slot); - if (rclick_type == CLICK_INPUT_SLOT || rclick_type == CLICK_OUTPUT_SLOT) { - - node_popup->clear(); - node_popup->set_size(Size2(1, 1)); - node_popup->add_item(TTR("Disconnect"), NODE_DISCONNECT); - if (anim_tree->node_get_type(rclick_node) == AnimationTreePlayer::NODE_TRANSITION) { - node_popup->add_item(TTR("Add Input"), NODE_ADD_INPUT); - if (rclick_type == CLICK_INPUT_SLOT) { - if (anim_tree->transition_node_has_input_auto_advance(rclick_node, rclick_slot)) - node_popup->add_item(TTR("Clear Auto-Advance"), NODE_CLEAR_AUTOADVANCE); - else - node_popup->add_item(TTR("Set Auto-Advance"), NODE_SET_AUTOADVANCE); - node_popup->add_item(TTR("Delete Input"), NODE_DELETE_INPUT); - } - } - - node_popup->set_position(rclick_pos + get_global_position()); - node_popup->popup(); - } - - if (rclick_type == CLICK_NODE) { - node_popup->clear(); - node_popup->set_size(Size2(1, 1)); - node_popup->add_item(TTR("Rename"), NODE_RENAME); - node_popup->add_item(TTR("Remove"), NODE_ERASE); - if (anim_tree->node_get_type(rclick_node) == AnimationTreePlayer::NODE_TRANSITION) - node_popup->add_item(TTR("Add Input"), NODE_ADD_INPUT); - node_popup->set_position(rclick_pos + get_global_position()); - node_popup->popup(); - } - } - } - } else { - - if (mb->get_button_index() == 1 && click_type != CLICK_NONE) { - - switch (click_type) { - case CLICK_INPUT_SLOT: - case CLICK_OUTPUT_SLOT: { - - Point2 dst_click_pos = Point2(mb->get_position().x, mb->get_position().y); - StringName id; - int slot; - ClickType dst_click_type = _locate_click(dst_click_pos, &id, &slot); - - if (dst_click_type == CLICK_INPUT_SLOT && click_type == CLICK_OUTPUT_SLOT) { - - anim_tree->connect_nodes(click_node, id, slot); - } - if (click_type == CLICK_INPUT_SLOT && dst_click_type == CLICK_OUTPUT_SLOT) { - - anim_tree->connect_nodes(id, click_node, click_slot); - } - - } break; - case CLICK_NODE: { - Point2 new_pos = anim_tree->node_get_position(click_node) + (click_motion - click_pos); - if (new_pos.x < 5) - new_pos.x = 5; - if (new_pos.y < 5) - new_pos.y = 5; - anim_tree->node_set_position(click_node, new_pos); - - } break; - default: {} - } - - click_type = CLICK_NONE; - update(); - } - } - } - - Ref<InputEventMouseMotion> mm = p_event; - - if (mm.is_valid()) { - - if (mm->get_button_mask() & 1 && click_type != CLICK_NONE) { - - click_motion = Point2(mm->get_position().x, mm->get_position().y); - update(); - } - if ((mm->get_button_mask() & 4 || Input::get_singleton()->is_key_pressed(KEY_SPACE))) { - - h_scroll->set_value(h_scroll->get_value() - mm->get_relative().x); - v_scroll->set_value(v_scroll->get_value() - mm->get_relative().y); - update(); - } - } + Vector<String> path = edited_path; + path.push_back(p_path); + edit_path(path); } -void AnimationTreeEditor::_draw_cos_line(const Vector2 &p_from, const Vector2 &p_to, const Color &p_color) { - - static const int steps = 20; - - Rect2 r; - r.position = p_from; - r.expand_to(p_to); - Vector2 sign = Vector2((p_from.x < p_to.x) ? 1 : -1, (p_from.y < p_to.y) ? 1 : -1); - bool flip = sign.x * sign.y < 0; - - Vector2 prev; - for (int i = 0; i <= steps; i++) { - - float d = i / float(steps); - float c = -Math::cos(d * Math_PI) * 0.5 + 0.5; - if (flip) - c = 1.0 - c; - Vector2 p = r.position + Vector2(d * r.size.width, c * r.size.height); - - if (i > 0) { - - draw_line(prev, p, p_color, 2); - } - - prev = p; - } +void AnimationTreeEditor::_about_to_show_root() { } void AnimationTreeEditor::_notification(int p_what) { + if (p_what == NOTIFICATION_PROCESS) { + ObjectID root = 0; + if (tree && tree->get_tree_root().is_valid()) { + root = tree->get_tree_root()->get_instance_id(); + } - switch (p_what) { - - case NOTIFICATION_ENTER_TREE: { - - play_button->set_icon(get_icon("Play", "EditorIcons")); - add_menu->set_icon(get_icon("Add", "EditorIcons")); - } break; - case NOTIFICATION_DRAW: { - - _update_scrollbars(); - //VisualServer::get_singleton()->canvas_item_add_rect(get_canvas_item(),Rect2(Point2(),get_size()),Color(0,0,0,1)); - get_stylebox("bg", "Tree")->draw(get_canvas_item(), Rect2(Point2(), get_size())); - - for (List<StringName>::Element *E = order.front(); E; E = E->next()) { - - _draw_node(E->get()); - } - - if (click_type == CLICK_INPUT_SLOT || click_type == CLICK_OUTPUT_SLOT) { - - _draw_cos_line(click_pos, click_motion, Color(0.5, 1, 0.5, 0.8)); - } - - List<AnimationTreePlayer::Connection> connections; - anim_tree->get_connection_list(&connections); - - for (List<AnimationTreePlayer::Connection>::Element *E = connections.front(); E; E = E->next()) { - - const AnimationTreePlayer::Connection &c = E->get(); - Point2 source = _get_slot_pos(c.src_node, false, 0); - Point2 dest = _get_slot_pos(c.dst_node, true, c.dst_input); - Color col = Color(1, 1, 0.5, 0.8); - /* - if (click_type==CLICK_NODE && click_node==c.src_node) { - - source+=click_motion-click_pos; - } - - if (click_type==CLICK_NODE && click_node==c.dst_node) { - - dest+=click_motion-click_pos; - }*/ - - _draw_cos_line(source, dest, col); - } - - switch (anim_tree->get_last_error()) { - - case AnimationTreePlayer::CONNECT_OK: { - - Ref<Font> f = get_font("font", "Label"); - f->draw(get_canvas_item(), Point2(5, 25 + f->get_ascent()), TTR("Animation tree is valid."), Color(0, 1, 0.6, 0.8)); - } break; - default: { - - Ref<Font> f = get_font("font", "Label"); - f->draw(get_canvas_item(), Point2(5, 25 + f->get_ascent()), TTR("Animation tree is invalid."), Color(1, 0.6, 0.0, 0.8)); - } break; - } - - } break; + if (root != current_root) { + edit_path(Vector<String>()); + } } } -void AnimationTreeEditor::_update_scrollbars() { - - Size2 size = get_size(); - Size2 hmin = h_scroll->get_combined_minimum_size(); - Size2 vmin = v_scroll->get_combined_minimum_size(); - - v_scroll->set_begin(Point2(size.width - vmin.width, 0)); - v_scroll->set_end(Point2(size.width, size.height)); - - h_scroll->set_begin(Point2(0, size.height - hmin.height)); - h_scroll->set_end(Point2(size.width - vmin.width, size.height)); - - Size2 min = _get_maximum_size(); - - if (min.height < size.height - hmin.height) { - - v_scroll->hide(); - offset.y = 0; - } else { - - v_scroll->show(); - v_scroll->set_max(min.height); - v_scroll->set_page(size.height - hmin.height); - offset.y = v_scroll->get_value(); - } - - if (min.width < size.width - vmin.width) { - - h_scroll->hide(); - offset.x = 0; - } else { - - h_scroll->show(); - h_scroll->set_max(min.width); - h_scroll->set_page(size.width - vmin.width); - offset.x = h_scroll->get_value(); - } +void AnimationTreeEditor::_bind_methods() { + ClassDB::bind_method("_path_button_pressed", &AnimationTreeEditor::_path_button_pressed); } -void AnimationTreeEditor::_scroll_moved(float) { +AnimationTreeEditor *AnimationTreeEditor::singleton = NULL; - offset.x = h_scroll->get_value(); - offset.y = v_scroll->get_value(); - update(); +void AnimationTreeEditor::add_plugin(AnimationTreeNodeEditorPlugin *p_editor) { + ERR_FAIL_COND(p_editor->get_parent()); + editor_base->add_child(p_editor); + editors.push_back(p_editor); + p_editor->set_h_size_flags(SIZE_EXPAND_FILL); + p_editor->set_v_size_flags(SIZE_EXPAND_FILL); + p_editor->hide(); } -void AnimationTreeEditor::_node_menu_item(int p_item) { - - switch (p_item) { - - case NODE_DISCONNECT: { - - if (rclick_type == CLICK_INPUT_SLOT) { - - anim_tree->disconnect_nodes(rclick_node, rclick_slot); - update(); - } - - if (rclick_type == CLICK_OUTPUT_SLOT) { - - List<AnimationTreePlayer::Connection> connections; - anim_tree->get_connection_list(&connections); - - for (List<AnimationTreePlayer::Connection>::Element *E = connections.front(); E; E = E->next()) { - - const AnimationTreePlayer::Connection &c = E->get(); - if (c.dst_node == rclick_node) { - - anim_tree->disconnect_nodes(c.dst_node, c.dst_input); - } - } - update(); - } - - } break; - case NODE_RENAME: { - - renaming_edit = true; - edited_node = rclick_node; - _popup_edit_dialog(); - - } break; - case NODE_ADD_INPUT: { - - anim_tree->transition_node_set_input_count(rclick_node, anim_tree->transition_node_get_input_count(rclick_node) + 1); - update(); - } break; - case NODE_DELETE_INPUT: { - - anim_tree->transition_node_delete_input(rclick_node, rclick_slot); - update(); - } break; - case NODE_SET_AUTOADVANCE: { - - anim_tree->transition_node_set_input_auto_advance(rclick_node, rclick_slot, true); - update(); - - } break; - case NODE_CLEAR_AUTOADVANCE: { - - anim_tree->transition_node_set_input_auto_advance(rclick_node, rclick_slot, false); - update(); - - } break; - - case NODE_ERASE: { - - if (rclick_node == "out") - break; - order.erase(rclick_node); - anim_tree->remove_node(rclick_node); - update(); - } break; - } +void AnimationTreeEditor::remove_plugin(AnimationTreeNodeEditorPlugin *p_editor) { + ERR_FAIL_COND(p_editor->get_parent() != editor_base); + editor_base->remove_child(p_editor); + editors.erase(p_editor); } -StringName AnimationTreeEditor::_add_node(int p_item) { - - static const char *bname[] = { - "out", - "anim", - "oneshot", - "mix", - "blend2", - "blend3", - "blend4", - "scale", - "seek", - "transition" - }; - - String name; - int idx = 1; - - while (true) { - - name = bname[p_item]; - if (idx > 1) - name += " " + itos(idx); - if (anim_tree->node_exists(name)) - idx++; - else - break; +String AnimationTreeEditor::get_base_path() { + String path = SceneStringNames::get_singleton()->parameters_base_path; + for (int i = 0; i < edited_path.size(); i++) { + path += edited_path[i] + "/"; } - - anim_tree->add_node((AnimationTreePlayer::NodeType)p_item, name); - anim_tree->node_set_position(name, Point2(last_x, last_y)); - order.push_back(name); - last_x += 10; - last_y += 10; - last_x = last_x % (int)get_size().width; - last_y = last_y % (int)get_size().height; - update(); - - return name; -}; - -void AnimationTreeEditor::_file_dialog_selected(String p_path) { - - switch (file_op) { - - case MENU_IMPORT_ANIMATIONS: { - Vector<String> files = file_dialog->get_selected_files(); - - for (int i = 0; i < files.size(); i++) { - - StringName node = _add_node(AnimationTreePlayer::NODE_ANIMATION); - - RES anim = ResourceLoader::load(files[i]); - anim_tree->animation_node_set_animation(node, anim); - //anim_tree->node_set_name(node, files[i].get_file()); - }; - } break; - - default: - break; - }; -}; - -void AnimationTreeEditor::_add_menu_item(int p_item) { - - if (p_item == MENU_GRAPH_CLEAR) { - - //clear - } else if (p_item == MENU_IMPORT_ANIMATIONS) { - - file_op = MENU_IMPORT_ANIMATIONS; - file_dialog->set_mode(EditorFileDialog::MODE_OPEN_FILE); - file_dialog->popup_centered_ratio(); - - } else { - - _add_node(p_item); - } -} - -Size2 AnimationTreeEditor::get_minimum_size() const { - - return Size2(10, 200); + return path; } -void AnimationTreeEditor::_find_paths_for_filter(const StringName &p_node, Set<String> &paths) { - - ERR_FAIL_COND(!anim_tree->node_exists(p_node)); - - for (int i = 0; i < anim_tree->node_get_input_count(p_node); i++) { - - StringName port = anim_tree->node_get_input_source(p_node, i); - if (port == StringName()) - continue; - _find_paths_for_filter(port, paths); - } - - if (anim_tree->node_get_type(p_node) == AnimationTreePlayer::NODE_ANIMATION) { - - Ref<Animation> anim = anim_tree->animation_node_get_animation(p_node); - if (anim.is_valid()) { - - for (int i = 0; i < anim->get_track_count(); i++) { - paths.insert(anim->track_get_path(i)); - } +bool AnimationTreeEditor::can_edit(const Ref<AnimationNode> &p_node) const { + for (int i = 0; i < editors.size(); i++) { + if (editors[i]->can_edit(p_node)) { + return true; } } + return false; } -void AnimationTreeEditor::_filter_edited() { - - TreeItem *ed = filter->get_edited(); - if (!ed) - return; +Vector<String> AnimationTreeEditor::get_animation_list() { - if (anim_tree->node_get_type(edited_node) == AnimationTreePlayer::NODE_ONESHOT) { - anim_tree->oneshot_node_set_filter_path(edited_node, ed->get_metadata(0), ed->is_checked(0)); - } else if (anim_tree->node_get_type(edited_node) == AnimationTreePlayer::NODE_BLEND2) { - anim_tree->blend2_node_set_filter_path(edited_node, ed->get_metadata(0), ed->is_checked(0)); - } else if (anim_tree->node_get_type(edited_node) == AnimationTreePlayer::NODE_ANIMATION) { - anim_tree->animation_node_set_filter_path(edited_node, ed->get_metadata(0), ed->is_checked(0)); + if (!singleton->is_visible()) { + return Vector<String>(); } -} - -void AnimationTreeEditor::_edit_filters() { - - filter_dialog->popup_centered_ratio(); - filter->clear(); - - Set<String> npb; - _find_paths_for_filter(edited_node, npb); - - TreeItem *root = filter->create_item(); - filter->set_hide_root(true); - Map<String, TreeItem *> pm; - - Node *base = anim_tree->get_node(anim_tree->get_base_path()); - - for (Set<String>::Element *E = npb.front(); E; E = E->next()) { - - TreeItem *parent = root; - String descr = E->get(); - if (base) { - NodePath np = E->get(); - if (np.get_subname_count() == 1) { - Node *n = base->get_node(np); - Skeleton *s = Object::cast_to<Skeleton>(n); - if (s) { + AnimationTree *tree = singleton->tree; + if (!tree || !tree->has_node(tree->get_animation_player())) + return Vector<String>(); - String skelbase = E->get().substr(0, E->get().find(":")); + AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(tree->get_node(tree->get_animation_player())); - int bidx = s->find_bone(np.get_subname(0)); + if (!ap) + return Vector<String>(); - if (bidx != -1) { - int bparent = s->get_bone_parent(bidx); - // - if (bparent != -1) { - - String bpn = skelbase + ":" + s->get_bone_name(bparent); - if (pm.has(bpn)) { - parent = pm[bpn]; - descr = np.get_subname(0); - } - } else { - - if (pm.has(skelbase)) { - parent = pm[skelbase]; - } - } - } - } - } - } - - TreeItem *it = filter->create_item(parent); - it->set_cell_mode(0, TreeItem::CELL_MODE_CHECK); - it->set_text(0, descr); - it->set_metadata(0, NodePath(E->get())); - it->set_editable(0, true); - if (anim_tree->node_get_type(edited_node) == AnimationTreePlayer::NODE_ONESHOT) { - it->set_checked(0, anim_tree->oneshot_node_is_path_filtered(edited_node, E->get())); - } else if (anim_tree->node_get_type(edited_node) == AnimationTreePlayer::NODE_BLEND2) { - it->set_checked(0, anim_tree->blend2_node_is_path_filtered(edited_node, E->get())); - } else if (anim_tree->node_get_type(edited_node) == AnimationTreePlayer::NODE_ANIMATION) { - it->set_checked(0, anim_tree->animation_node_is_path_filtered(edited_node, E->get())); - } - pm[E->get()] = it; + List<StringName> anims; + ap->get_animation_list(&anims); + Vector<String> ret; + for (List<StringName>::Element *E = anims.front(); E; E = E->next()) { + ret.push_back(E->get()); } -} - -void AnimationTreeEditor::_bind_methods() { - ClassDB::bind_method("_add_menu_item", &AnimationTreeEditor::_add_menu_item); - ClassDB::bind_method("_node_menu_item", &AnimationTreeEditor::_node_menu_item); - ClassDB::bind_method("_gui_input", &AnimationTreeEditor::_gui_input); - //ClassDB::bind_method( "_node_param_changed", &AnimationTreeEditor::_node_param_changed ); - ClassDB::bind_method("_scroll_moved", &AnimationTreeEditor::_scroll_moved); - ClassDB::bind_method("_edit_dialog_changeds", &AnimationTreeEditor::_edit_dialog_changeds); - ClassDB::bind_method("_edit_dialog_changede", &AnimationTreeEditor::_edit_dialog_changede); - ClassDB::bind_method("_edit_dialog_changedf", &AnimationTreeEditor::_edit_dialog_changedf); - ClassDB::bind_method("_edit_dialog_changed", &AnimationTreeEditor::_edit_dialog_changed); - ClassDB::bind_method("_edit_dialog_animation_changed", &AnimationTreeEditor::_edit_dialog_animation_changed); - ClassDB::bind_method("_edit_dialog_edit_animation", &AnimationTreeEditor::_edit_dialog_edit_animation); - ClassDB::bind_method("_play_toggled", &AnimationTreeEditor::_play_toggled); - ClassDB::bind_method("_edit_oneshot_start", &AnimationTreeEditor::_edit_oneshot_start); - ClassDB::bind_method("_file_dialog_selected", &AnimationTreeEditor::_file_dialog_selected); - ClassDB::bind_method("_master_anim_menu_item", &AnimationTreeEditor::_master_anim_menu_item); - ClassDB::bind_method("_edit_filters", &AnimationTreeEditor::_edit_filters); - ClassDB::bind_method("_filter_edited", &AnimationTreeEditor::_filter_edited); + return ret; } AnimationTreeEditor::AnimationTreeEditor() { - set_focus_mode(FOCUS_ALL); - - PopupMenu *p; - List<PropertyInfo> defaults; - - add_menu = memnew(MenuButton); - //add_menu->set_ - add_menu->set_position(Point2(0, 0)); - add_menu->set_size(Point2(25, 15)); - add_child(add_menu); - - p = add_menu->get_popup(); - p->add_item(TTR("Animation Node"), AnimationTreePlayer::NODE_ANIMATION); - p->add_item(TTR("OneShot Node"), AnimationTreePlayer::NODE_ONESHOT); - p->add_item(TTR("Mix Node"), AnimationTreePlayer::NODE_MIX); - p->add_item(TTR("Blend2 Node"), AnimationTreePlayer::NODE_BLEND2); - p->add_item(TTR("Blend3 Node"), AnimationTreePlayer::NODE_BLEND3); - p->add_item(TTR("Blend4 Node"), AnimationTreePlayer::NODE_BLEND4); - p->add_item(TTR("TimeScale Node"), AnimationTreePlayer::NODE_TIMESCALE); - p->add_item(TTR("TimeSeek Node"), AnimationTreePlayer::NODE_TIMESEEK); - p->add_item(TTR("Transition Node"), AnimationTreePlayer::NODE_TRANSITION); - p->add_separator(); - p->add_item(TTR("Import Animations..."), MENU_IMPORT_ANIMATIONS); // wtf - p->add_separator(); - p->add_item(TTR("Clear"), MENU_GRAPH_CLEAR); - - p->connect("id_pressed", this, "_add_menu_item"); - - play_button = memnew(Button); - play_button->set_position(Point2(25, 0)); - play_button->set_size(Point2(25, 15)); - add_child(play_button); - play_button->set_toggle_mode(true); - play_button->connect("pressed", this, "_play_toggled"); - - last_x = 50; - last_y = 50; - - property_editor = memnew(CustomPropertyEditor); - add_child(property_editor); - property_editor->connect("variant_changed", this, "_edit_dialog_animation_changed"); - property_editor->connect("resource_edit_request", this, "_edit_dialog_edit_animation"); - - h_scroll = memnew(HScrollBar); - v_scroll = memnew(VScrollBar); - - add_child(h_scroll); - add_child(v_scroll); - - h_scroll->connect("value_changed", this, "_scroll_moved"); - v_scroll->connect("value_changed", this, "_scroll_moved"); - - node_popup = memnew(PopupMenu); - add_child(node_popup); - node_popup->set_as_toplevel(true); - - master_anim_popup = memnew(PopupMenu); - add_child(master_anim_popup); - master_anim_popup->connect("id_pressed", this, "_master_anim_menu_item"); - - node_popup->connect("id_pressed", this, "_node_menu_item"); - - updating_edit = false; - - edit_dialog = memnew(PopupPanel); - //edit_dialog->get_ok()->hide(); - //edit_dialog->get_cancel()->hide(); - add_child(edit_dialog); - - edit_option = memnew(OptionButton); - edit_option->set_anchor(MARGIN_RIGHT, ANCHOR_END); - edit_option->set_margin(MARGIN_RIGHT, -10); - edit_dialog->add_child(edit_option); - edit_option->connect("item_selected", this, "_edit_dialog_changedf"); - edit_option->hide(); - - for (int i = 0; i < 2; i++) { - edit_scroll[i] = memnew(HSlider); - edit_scroll[i]->set_anchor(MARGIN_RIGHT, ANCHOR_END); - edit_scroll[i]->set_margin(MARGIN_RIGHT, -10); - edit_dialog->add_child(edit_scroll[i]); - edit_scroll[i]->hide(); - edit_scroll[i]->connect("value_changed", this, "_edit_dialog_changedf"); - } - for (int i = 0; i < 4; i++) { - edit_line[i] = memnew(LineEdit); - edit_line[i]->set_anchor(MARGIN_RIGHT, ANCHOR_END); - edit_line[i]->set_margin(MARGIN_RIGHT, -10); - edit_dialog->add_child(edit_line[i]); - edit_line[i]->hide(); - edit_line[i]->connect("text_changed", this, "_edit_dialog_changeds"); - edit_line[i]->connect("text_entered", this, "_edit_dialog_changede"); - edit_label[i] = memnew(Label); - edit_dialog->add_child(edit_label[i]); - edit_label[i]->hide(); - } - - edit_button = memnew(Button); - edit_button->set_anchor(MARGIN_RIGHT, ANCHOR_END); - edit_button->set_margin(MARGIN_RIGHT, -10); - edit_dialog->add_child(edit_button); - edit_button->hide(); - edit_button->connect("pressed", this, "_edit_oneshot_start"); - - edit_check = memnew(CheckButton); - edit_check->set_anchor(MARGIN_RIGHT, ANCHOR_END); - edit_check->set_margin(MARGIN_RIGHT, -10); - edit_dialog->add_child(edit_check); - edit_check->hide(); - edit_check->connect("pressed", this, "_edit_dialog_changed"); - - file_dialog = memnew(EditorFileDialog); - file_dialog->set_enable_multiple_selection(true); - file_dialog->set_current_dir(ProjectSettings::get_singleton()->get_resource_path()); - add_child(file_dialog); - file_dialog->connect("file_selected", this, "_file_dialog_selected"); - - filter_dialog = memnew(AcceptDialog); - filter_dialog->set_title(TTR("Edit Node Filters")); - add_child(filter_dialog); - - filter = memnew(Tree); - filter_dialog->add_child(filter); - //filter_dialog->set_child_rect(filter); - filter->connect("item_edited", this, "_filter_edited"); + AnimationNodeAnimation::get_editable_animation_list = get_animation_list; + path_edit = memnew(ScrollContainer); + add_child(path_edit); + path_edit->set_enable_h_scroll(true); + path_edit->set_enable_v_scroll(false); + path_hb = memnew(HBoxContainer); + path_edit->add_child(path_hb); - filter_button = memnew(Button); - filter_button->set_anchor(MARGIN_RIGHT, ANCHOR_END); - filter_button->set_margin(MARGIN_RIGHT, -10); - edit_dialog->add_child(filter_button); - filter_button->hide(); - filter_button->set_text(TTR("Filters...")); - filter_button->connect("pressed", this, "_edit_filters"); + current_root = 0; + singleton = this; + editor_base = memnew(PanelContainer); + editor_base->set_v_size_flags(SIZE_EXPAND_FILL); + add_child(editor_base); - set_clip_contents(true); + add_plugin(memnew(AnimationNodeBlendTreeEditor)); + add_plugin(memnew(AnimationNodeBlendSpace1DEditor)); + add_plugin(memnew(AnimationNodeBlendSpace2DEditor)); + add_plugin(memnew(AnimationNodeStateMachineEditor)); } void AnimationTreeEditorPlugin::edit(Object *p_object) { - anim_tree_editor->edit(Object::cast_to<AnimationTreePlayer>(p_object)); + anim_tree_editor->edit(Object::cast_to<AnimationTree>(p_object)); } bool AnimationTreeEditorPlugin::handles(Object *p_object) const { - return p_object->is_class("AnimationTreePlayer"); + return p_object->is_class("AnimationTree"); } void AnimationTreeEditorPlugin::make_visible(bool p_visible) { @@ -1422,13 +251,13 @@ void AnimationTreeEditorPlugin::make_visible(bool p_visible) { //editor->animation_panel_make_visible(true); button->show(); editor->make_bottom_panel_item_visible(anim_tree_editor); - anim_tree_editor->set_physics_process(true); + anim_tree_editor->set_process(true); } else { if (anim_tree_editor->is_visible_in_tree()) editor->hide_bottom_panel(); button->hide(); - anim_tree_editor->set_physics_process(false); + anim_tree_editor->set_process(false); } } diff --git a/editor/plugins/animation_tree_editor_plugin.h b/editor/plugins/animation_tree_editor_plugin.h index aeb5b1744f..b12054bb62 100644 --- a/editor/plugins/animation_tree_editor_plugin.h +++ b/editor/plugins/animation_tree_editor_plugin.h @@ -1,167 +1,65 @@ -/*************************************************************************/ -/* animation_tree_editor_plugin.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - #ifndef ANIMATION_TREE_EDITOR_PLUGIN_H #define ANIMATION_TREE_EDITOR_PLUGIN_H #include "editor/editor_node.h" #include "editor/editor_plugin.h" #include "editor/property_editor.h" -#include "scene/animation/animation_tree_player.h" +#include "scene/animation/animation_tree.h" #include "scene/gui/button.h" +#include "scene/gui/graph_edit.h" #include "scene/gui/popup.h" #include "scene/gui/tree.h" -/** - @author Juan Linietsky <reduzio@gmail.com> -*/ - -class AnimationTreeEditor : public Control { - - GDCLASS(AnimationTreeEditor, Control); - - static const char *_node_type_names[]; - - enum ClickType { - CLICK_NONE, - CLICK_NAME, - CLICK_NODE, - CLICK_INPUT_SLOT, - CLICK_OUTPUT_SLOT, - CLICK_PARAMETER - }; - - enum { - - MENU_GRAPH_CLEAR = 100, - MENU_IMPORT_ANIMATIONS = 101, - NODE_DISCONNECT, - NODE_RENAME, - NODE_ERASE, - NODE_ADD_INPUT, - NODE_DELETE_INPUT, - NODE_SET_AUTOADVANCE, - NODE_CLEAR_AUTOADVANCE - }; - - bool renaming_edit; - StringName edited_node; - bool updating_edit; - Popup *edit_dialog; - HSlider *edit_scroll[2]; - LineEdit *edit_line[4]; - OptionButton *edit_option; - Label *edit_label[4]; - Button *edit_button; - Button *filter_button; - CheckButton *edit_check; - EditorFileDialog *file_dialog; - int file_op; - - void _popup_edit_dialog(); - - void _setup_edit_dialog(const StringName &p_node); - PopupMenu *master_anim_popup; - PopupMenu *node_popup; - PopupMenu *add_popup; - HScrollBar *h_scroll; - VScrollBar *v_scroll; - MenuButton *add_menu; - - CustomPropertyEditor *property_editor; - - AnimationTreePlayer *anim_tree; - List<StringName> order; - Set<StringName> active_nodes; - - int last_x, last_y; - - Point2 offset; - ClickType click_type; - Point2 click_pos; - StringName click_node; - int click_slot; - Point2 click_motion; - ClickType rclick_type; - StringName rclick_node; - int rclick_slot; - - Button *play_button; - - Size2 _get_maximum_size(); - Size2 get_node_size(const StringName &p_node) const; - void _draw_node(const StringName &p_node); - - AcceptDialog *filter_dialog; - Tree *filter; - - void _draw_cos_line(const Vector2 &p_from, const Vector2 &p_to, const Color &p_color); - void _update_scrollbars(); - void _scroll_moved(float); - void _play_toggled(); - /* - void _node_param_changed(); - void _node_add_callback(); - void _node_add(VisualServer::AnimationTreeNodeType p_type); - void _node_edit_property(const StringName& p_node); -*/ - - void _master_anim_menu_item(int p_item); - void _node_menu_item(int p_item); - void _add_menu_item(int p_item); - - void _filter_edited(); - void _find_paths_for_filter(const StringName &p_node, Set<String> &paths); - void _edit_filters(); - - void _edit_oneshot_start(); - void _edit_dialog_animation_changed(); - void _edit_dialog_edit_animation(); - void _edit_dialog_changeds(String); - void _edit_dialog_changede(String); - void _edit_dialog_changedf(float); - void _edit_dialog_changed(); - void _dialog_changed() const; - ClickType _locate_click(const Point2 &p_click, StringName *p_node_id, int *p_slot_index) const; - Point2 _get_slot_pos(const StringName &p_node_id, bool p_input, int p_slot); - - StringName _add_node(int p_item); - void _file_dialog_selected(String p_path); + +class AnimationTreeNodeEditorPlugin : public VBoxContainer { + GDCLASS(AnimationTreeNodeEditorPlugin, VBoxContainer) +public: + virtual bool can_edit(const Ref<AnimationNode> &p_node) = 0; + virtual void edit(const Ref<AnimationNode> &p_node) = 0; +}; + +class AnimationTreeEditor : public VBoxContainer { + + GDCLASS(AnimationTreeEditor, VBoxContainer); + + ScrollContainer *path_edit; + HBoxContainer *path_hb; + + AnimationTree *tree; + PanelContainer *editor_base; + + Vector<String> button_path; + Vector<String> edited_path; + Vector<AnimationTreeNodeEditorPlugin *> editors; + + void _update_path(); + void _about_to_show_root(); + ObjectID current_root; + + void _path_button_pressed(int p_path); + + static Vector<String> get_animation_list(); protected: void _notification(int p_what); - void _gui_input(Ref<InputEvent> p_event); static void _bind_methods(); + static AnimationTreeEditor *singleton; + public: - virtual Size2 get_minimum_size() const; - void edit(AnimationTreePlayer *p_anim_tree); + AnimationTree *get_tree() { return tree; } + void add_plugin(AnimationTreeNodeEditorPlugin *p_editor); + void remove_plugin(AnimationTreeNodeEditorPlugin *p_editor); + + String get_base_path(); + + bool can_edit(const Ref<AnimationNode> &p_node) const; + + void edit_path(const Vector<String> &p_path); + Vector<String> get_edited_path() const; + + void enter_editor(const String &p_path = ""); + static AnimationTreeEditor *get_singleton() { return singleton; } + void edit(AnimationTree *p_tree); AnimationTreeEditor(); }; @@ -174,7 +72,7 @@ class AnimationTreeEditorPlugin : public EditorPlugin { Button *button; public: - virtual String get_name() const { return "AnimTree"; } + virtual String get_name() const { return "AnimationTree"; } bool has_main_screen() const { return false; } virtual void edit(Object *p_object); virtual bool handles(Object *p_object) const; diff --git a/editor/plugins/animation_tree_player_editor_plugin.cpp b/editor/plugins/animation_tree_player_editor_plugin.cpp new file mode 100644 index 0000000000..36d10ab99e --- /dev/null +++ b/editor/plugins/animation_tree_player_editor_plugin.cpp @@ -0,0 +1,1446 @@ +/*************************************************************************/ +/* animation_tree_editor_plugin.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "animation_tree_player_editor_plugin.h" + +#include "core/io/resource_loader.h" +#include "core/project_settings.h" +#include "os/input.h" +#include "os/keyboard.h" +#include "scene/gui/menu_button.h" +#include "scene/gui/panel.h" +#include "scene/main/viewport.h" + +void AnimationTreePlayerEditor::edit(AnimationTreePlayer *p_anim_tree) { + + anim_tree = p_anim_tree; + + if (!anim_tree) { + hide(); + } else { + order.clear(); + p_anim_tree->get_node_list(&order); + /* + for(List<StringName>::Element* E=order.front();E;E=E->next()) { + + if (E->get() >= (int)last_id) + last_id=E->get()+1; + }*/ + play_button->set_pressed(p_anim_tree->is_active()); + //read the orders + } +} + +Size2 AnimationTreePlayerEditor::_get_maximum_size() { + + Size2 max; + + for (List<StringName>::Element *E = order.front(); E; E = E->next()) { + + Point2 pos = anim_tree->node_get_position(E->get()); + + if (click_type == CLICK_NODE && click_node == E->get()) { + + pos += click_motion - click_pos; + } + pos += get_node_size(E->get()); + if (pos.x > max.x) + max.x = pos.x; + if (pos.y > max.y) + max.y = pos.y; + } + + return max; +} + +const char *AnimationTreePlayerEditor::_node_type_names[] = { "Output", "Animation", "OneShot", "Mix", "Blend2", "Blend3", "Blend4", "TimeScale", "TimeSeek", "Transition" }; + +Size2 AnimationTreePlayerEditor::get_node_size(const StringName &p_node) const { + + AnimationTreePlayer::NodeType type = anim_tree->node_get_type(p_node); + + Ref<StyleBox> style = get_stylebox("panel", "PopupMenu"); + Ref<Font> font = get_font("font", "PopupMenu"); + + Size2 size = style->get_minimum_size(); + + int count = 2; // title and name + int inputs = anim_tree->node_get_input_count(p_node); + count += inputs ? inputs : 1; + String name = p_node; + + float name_w = font->get_string_size(name).width; + float type_w = font->get_string_size(String(_node_type_names[type])).width; + float max_w = MAX(name_w, type_w); + + switch (type) { + case AnimationTreePlayer::NODE_TIMESEEK: + case AnimationTreePlayer::NODE_OUTPUT: { + } break; + case AnimationTreePlayer::NODE_ANIMATION: + case AnimationTreePlayer::NODE_ONESHOT: + case AnimationTreePlayer::NODE_MIX: + case AnimationTreePlayer::NODE_BLEND2: + case AnimationTreePlayer::NODE_BLEND3: + case AnimationTreePlayer::NODE_BLEND4: + case AnimationTreePlayer::NODE_TIMESCALE: + case AnimationTreePlayer::NODE_TRANSITION: { + + size.height += font->get_height(); + } break; + case AnimationTreePlayer::NODE_MAX: { + } + } + + size.x += max_w + 20; + size.y += count * (font->get_height() + get_constant("vseparation", "PopupMenu")); + + return size; +} + +void AnimationTreePlayerEditor::_edit_dialog_changede(String) { + + edit_dialog->hide(); +} + +void AnimationTreePlayerEditor::_edit_dialog_changeds(String s) { + + _edit_dialog_changed(); +} + +void AnimationTreePlayerEditor::_edit_dialog_changedf(float) { + + _edit_dialog_changed(); +} + +void AnimationTreePlayerEditor::_edit_dialog_changed() { + + if (updating_edit) + return; + + if (renaming_edit) { + + if (anim_tree->node_rename(edited_node, edit_line[0]->get_text()) == OK) { + for (List<StringName>::Element *E = order.front(); E; E = E->next()) { + + if (E->get() == edited_node) + E->get() = edit_line[0]->get_text(); + } + edited_node = edit_line[0]->get_text(); + } + update(); + return; + } + + AnimationTreePlayer::NodeType type = anim_tree->node_get_type(edited_node); + + switch (type) { + + case AnimationTreePlayer::NODE_TIMESCALE: + anim_tree->timescale_node_set_scale(edited_node, edit_line[0]->get_text().to_double()); + break; + case AnimationTreePlayer::NODE_ONESHOT: + anim_tree->oneshot_node_set_fadein_time(edited_node, edit_line[0]->get_text().to_double()); + anim_tree->oneshot_node_set_fadeout_time(edited_node, edit_line[1]->get_text().to_double()); + anim_tree->oneshot_node_set_autorestart_delay(edited_node, edit_line[2]->get_text().to_double()); + anim_tree->oneshot_node_set_autorestart_random_delay(edited_node, edit_line[3]->get_text().to_double()); + anim_tree->oneshot_node_set_autorestart(edited_node, edit_check->is_pressed()); + anim_tree->oneshot_node_set_mix_mode(edited_node, edit_option->get_selected()); + + break; + + case AnimationTreePlayer::NODE_MIX: + + anim_tree->mix_node_set_amount(edited_node, edit_scroll[0]->get_value()); + break; + case AnimationTreePlayer::NODE_BLEND2: + anim_tree->blend2_node_set_amount(edited_node, edit_scroll[0]->get_value()); + + break; + + case AnimationTreePlayer::NODE_BLEND3: + anim_tree->blend3_node_set_amount(edited_node, edit_scroll[0]->get_value()); + + break; + case AnimationTreePlayer::NODE_BLEND4: + + anim_tree->blend4_node_set_amount(edited_node, Point2(edit_scroll[0]->get_value(), edit_scroll[1]->get_value())); + + break; + + case AnimationTreePlayer::NODE_TRANSITION: { + anim_tree->transition_node_set_xfade_time(edited_node, edit_line[0]->get_text().to_double()); + if (anim_tree->transition_node_get_current(edited_node) != edit_option->get_selected()) + anim_tree->transition_node_set_current(edited_node, edit_option->get_selected()); + } break; + default: {} + } +} + +void AnimationTreePlayerEditor::_edit_dialog_animation_changed() { + + Ref<Animation> anim = property_editor->get_variant().operator RefPtr(); + anim_tree->animation_node_set_animation(edited_node, anim); + update(); +} + +void AnimationTreePlayerEditor::_edit_dialog_edit_animation() { + + if (Engine::get_singleton()->is_editor_hint()) { + get_tree()->get_root()->get_child(0)->call("_resource_selected", property_editor->get_variant().operator RefPtr()); + }; +}; + +void AnimationTreePlayerEditor::_edit_oneshot_start() { + + anim_tree->oneshot_node_start(edited_node); +} + +void AnimationTreePlayerEditor::_play_toggled() { + + anim_tree->set_active(play_button->is_pressed()); +} + +void AnimationTreePlayerEditor::_master_anim_menu_item(int p_item) { + + if (p_item == 0) + _edit_filters(); + else { + + String str = master_anim_popup->get_item_text(p_item); + anim_tree->animation_node_set_master_animation(edited_node, str); + } + update(); +} + +void AnimationTreePlayerEditor::_popup_edit_dialog() { + + updating_edit = true; + + for (int i = 0; i < 2; i++) + edit_scroll[i]->hide(); + + for (int i = 0; i < 4; i++) { + + edit_line[i]->hide(); + edit_label[i]->hide(); + } + + edit_option->hide(); + edit_button->hide(); + filter_button->hide(); + edit_check->hide(); + + Point2 pos = anim_tree->node_get_position(edited_node) - Point2(h_scroll->get_value(), v_scroll->get_value()); + Ref<StyleBox> style = get_stylebox("panel", "PopupMenu"); + Size2 size = get_node_size(edited_node); + Point2 popup_pos(pos.x + style->get_margin(MARGIN_LEFT), pos.y + size.y - style->get_margin(MARGIN_BOTTOM)); + popup_pos += get_global_position(); + + if (renaming_edit) { + + edit_label[0]->set_text(TTR("New name:")); + edit_label[0]->set_position(Point2(5, 5)); + edit_label[0]->show(); + edit_line[0]->set_begin(Point2(15, 25)); + edit_line[0]->set_text(edited_node); + edit_line[0]->show(); + edit_dialog->set_size(Size2(150, 50)); + + } else { + + AnimationTreePlayer::NodeType type = anim_tree->node_get_type(edited_node); + + switch (type) { + + case AnimationTreePlayer::NODE_ANIMATION: + + if (anim_tree->get_master_player() != NodePath() && anim_tree->has_node(anim_tree->get_master_player()) && Object::cast_to<AnimationPlayer>(anim_tree->get_node(anim_tree->get_master_player()))) { + + AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(anim_tree->get_node(anim_tree->get_master_player())); + master_anim_popup->clear(); + master_anim_popup->add_item(TTR("Edit Filters")); + master_anim_popup->add_separator(); + List<StringName> sn; + ap->get_animation_list(&sn); + sn.sort_custom<StringName::AlphCompare>(); + for (List<StringName>::Element *E = sn.front(); E; E = E->next()) { + master_anim_popup->add_item(E->get()); + } + + master_anim_popup->set_position(popup_pos); + master_anim_popup->popup(); + } else { + property_editor->edit(this, "", Variant::OBJECT, anim_tree->animation_node_get_animation(edited_node), PROPERTY_HINT_RESOURCE_TYPE, "Animation"); + property_editor->set_position(popup_pos); + property_editor->popup(); + updating_edit = false; + } + return; + case AnimationTreePlayer::NODE_TIMESCALE: + edit_label[0]->set_text(TTR("Scale:")); + edit_label[0]->set_position(Point2(5, 5)); + edit_label[0]->show(); + edit_line[0]->set_begin(Point2(15, 25)); + edit_line[0]->set_text(rtos(anim_tree->timescale_node_get_scale(edited_node))); + edit_line[0]->show(); + edit_dialog->set_size(Size2(150, 50)); + break; + case AnimationTreePlayer::NODE_ONESHOT: + edit_label[0]->set_text(TTR("Fade In (s):")); + edit_label[0]->set_position(Point2(5, 5)); + edit_label[0]->show(); + edit_line[0]->set_begin(Point2(15, 25)); + edit_line[0]->set_text(rtos(anim_tree->oneshot_node_get_fadein_time(edited_node))); + edit_line[0]->show(); + edit_label[1]->set_text(TTR("Fade Out (s):")); + edit_label[1]->set_position(Point2(5, 55)); + edit_label[1]->show(); + edit_line[1]->set_begin(Point2(15, 75)); + edit_line[1]->set_text(rtos(anim_tree->oneshot_node_get_fadeout_time(edited_node))); + edit_line[1]->show(); + + edit_option->clear(); + edit_option->add_item(TTR("Blend"), 0); + edit_option->add_item(TTR("Mix"), 1); + edit_option->set_begin(Point2(15, 105)); + + edit_option->select(anim_tree->oneshot_node_get_mix_mode(edited_node)); + edit_option->show(); + + edit_check->set_text(TTR("Auto Restart:")); + edit_check->set_begin(Point2(15, 125)); + edit_check->set_pressed(anim_tree->oneshot_node_has_autorestart(edited_node)); + edit_check->show(); + + edit_label[2]->set_text(TTR("Restart (s):")); + edit_label[2]->set_position(Point2(5, 145)); + edit_label[2]->show(); + edit_line[2]->set_begin(Point2(15, 165)); + edit_line[2]->set_text(rtos(anim_tree->oneshot_node_get_autorestart_delay(edited_node))); + edit_line[2]->show(); + edit_label[3]->set_text(TTR("Random Restart (s):")); + edit_label[3]->set_position(Point2(5, 195)); + edit_label[3]->show(); + edit_line[3]->set_begin(Point2(15, 215)); + edit_line[3]->set_text(rtos(anim_tree->oneshot_node_get_autorestart_random_delay(edited_node))); + edit_line[3]->show(); + + filter_button->set_begin(Point2(10, 245)); + filter_button->show(); + + edit_button->set_begin(Point2(10, 268)); + edit_button->set_text(TTR("Start!")); + + edit_button->show(); + + edit_dialog->set_size(Size2(180, 293)); + + break; + + case AnimationTreePlayer::NODE_MIX: + + edit_label[0]->set_text(TTR("Amount:")); + edit_label[0]->set_position(Point2(5, 5)); + edit_label[0]->show(); + edit_scroll[0]->set_min(0); + edit_scroll[0]->set_max(1); + edit_scroll[0]->set_step(0.01); + edit_scroll[0]->set_value(anim_tree->mix_node_get_amount(edited_node)); + edit_scroll[0]->set_begin(Point2(15, 25)); + edit_scroll[0]->show(); + edit_dialog->set_size(Size2(150, 50)); + + break; + case AnimationTreePlayer::NODE_BLEND2: + edit_label[0]->set_text(TTR("Blend:")); + edit_label[0]->set_position(Point2(5, 5)); + edit_label[0]->show(); + edit_scroll[0]->set_min(0); + edit_scroll[0]->set_max(1); + edit_scroll[0]->set_step(0.01); + edit_scroll[0]->set_value(anim_tree->blend2_node_get_amount(edited_node)); + edit_scroll[0]->set_begin(Point2(15, 25)); + edit_scroll[0]->show(); + filter_button->set_begin(Point2(10, 47)); + filter_button->show(); + edit_dialog->set_size(Size2(150, 74)); + + break; + + case AnimationTreePlayer::NODE_BLEND3: + edit_label[0]->set_text(TTR("Blend:")); + edit_label[0]->set_position(Point2(5, 5)); + edit_label[0]->show(); + edit_scroll[0]->set_min(-1); + edit_scroll[0]->set_max(1); + edit_scroll[0]->set_step(0.01); + edit_scroll[0]->set_value(anim_tree->blend3_node_get_amount(edited_node)); + edit_scroll[0]->set_begin(Point2(15, 25)); + edit_scroll[0]->show(); + edit_dialog->set_size(Size2(150, 50)); + + break; + case AnimationTreePlayer::NODE_BLEND4: + + edit_label[0]->set_text(TTR("Blend 0:")); + edit_label[0]->set_position(Point2(5, 5)); + edit_label[0]->show(); + edit_scroll[0]->set_min(0); + edit_scroll[0]->set_max(1); + edit_scroll[0]->set_step(0.01); + edit_scroll[0]->set_value(anim_tree->blend4_node_get_amount(edited_node).x); + edit_scroll[0]->set_begin(Point2(15, 25)); + edit_scroll[0]->show(); + edit_label[1]->set_text(TTR("Blend 1:")); + edit_label[1]->set_position(Point2(5, 55)); + edit_label[1]->show(); + edit_scroll[1]->set_min(0); + edit_scroll[1]->set_max(1); + edit_scroll[1]->set_step(0.01); + edit_scroll[1]->set_value(anim_tree->blend4_node_get_amount(edited_node).y); + edit_scroll[1]->set_begin(Point2(15, 75)); + edit_scroll[1]->show(); + edit_dialog->set_size(Size2(150, 100)); + + break; + + case AnimationTreePlayer::NODE_TRANSITION: { + + edit_label[0]->set_text(TTR("X-Fade Time (s):")); + edit_label[0]->set_position(Point2(5, 5)); + edit_label[0]->show(); + edit_line[0]->set_begin(Point2(15, 25)); + edit_line[0]->set_text(rtos(anim_tree->transition_node_get_xfade_time(edited_node))); + edit_line[0]->show(); + + edit_label[1]->set_text(TTR("Current:")); + edit_label[1]->set_position(Point2(5, 55)); + edit_label[1]->show(); + edit_option->set_begin(Point2(15, 75)); + + edit_option->clear(); + + for (int i = 0; i < anim_tree->transition_node_get_input_count(edited_node); i++) { + edit_option->add_item(itos(i), i); + } + + edit_option->select(anim_tree->transition_node_get_current(edited_node)); + edit_option->show(); + edit_dialog->set_size(Size2(150, 100)); + + } break; + default: {} + } + } + + edit_dialog->set_position(popup_pos); + edit_dialog->popup(); + + updating_edit = false; +} + +void AnimationTreePlayerEditor::_draw_node(const StringName &p_node) { + + RID ci = get_canvas_item(); + AnimationTreePlayer::NodeType type = anim_tree->node_get_type(p_node); + + Ref<StyleBox> style = get_stylebox("panel", "PopupMenu"); + Ref<Font> font = get_font("font", "PopupMenu"); + Color font_color = get_color("font_color", "PopupMenu"); + Color font_color_title = get_color("font_color_hover", "PopupMenu"); + font_color_title.a *= 0.8; + Ref<Texture> slot_icon = get_icon("VisualShaderPort", "EditorIcons"); + + Size2 size = get_node_size(p_node); + Point2 pos = anim_tree->node_get_position(p_node); + if (click_type == CLICK_NODE && click_node == p_node) { + + pos += click_motion - click_pos; + if (pos.x < 5) + pos.x = 5; + if (pos.y < 5) + pos.y = 5; + } + + pos -= Point2(h_scroll->get_value(), v_scroll->get_value()); + + style->draw(ci, Rect2(pos, size)); + + float w = size.width - style->get_minimum_size().width; + float h = font->get_height() + get_constant("vseparation", "PopupMenu"); + + Point2 ofs = style->get_offset() + pos; + Point2 ascofs(0, font->get_ascent()); + + Color bx = font_color_title; + bx.a *= 0.1; + draw_rect(Rect2(ofs, Size2(size.width - style->get_minimum_size().width, font->get_height())), bx); + font->draw_halign(ci, ofs + ascofs, HALIGN_CENTER, w, String(_node_type_names[type]), font_color_title); + + ofs.y += h; + font->draw_halign(ci, ofs + ascofs, HALIGN_CENTER, w, p_node, font_color); + ofs.y += h; + + int count = 2; // title and name + int inputs = anim_tree->node_get_input_count(p_node); + count += inputs ? inputs : 1; + + float icon_h_ofs = Math::floor((font->get_height() - slot_icon->get_height()) / 2.0) + 1; + + if (type != AnimationTreePlayer::NODE_OUTPUT) + slot_icon->draw(ci, ofs + Point2(w, icon_h_ofs)); //output + + if (inputs) { + for (int i = 0; i < inputs; i++) { + + slot_icon->draw(ci, ofs + Point2(-slot_icon->get_width(), icon_h_ofs)); + String text; + switch (type) { + + case AnimationTreePlayer::NODE_TIMESCALE: + case AnimationTreePlayer::NODE_TIMESEEK: text = "in"; break; + case AnimationTreePlayer::NODE_OUTPUT: text = "out"; break; + case AnimationTreePlayer::NODE_ANIMATION: break; + case AnimationTreePlayer::NODE_ONESHOT: text = (i == 0 ? "in" : "add"); break; + case AnimationTreePlayer::NODE_BLEND2: + case AnimationTreePlayer::NODE_MIX: text = (i == 0 ? "a" : "b"); break; + case AnimationTreePlayer::NODE_BLEND3: + switch (i) { + case 0: text = "b-"; break; + case 1: text = "a"; break; + case 2: text = "b+"; break; + } + break; + + case AnimationTreePlayer::NODE_BLEND4: + switch (i) { + case 0: text = "a0"; break; + case 1: text = "b0"; break; + case 2: text = "a1"; break; + case 3: text = "b1"; break; + } + break; + + case AnimationTreePlayer::NODE_TRANSITION: + text = itos(i); + if (anim_tree->transition_node_has_input_auto_advance(p_node, i)) + text += "->"; + + break; + default: {} + } + font->draw(ci, ofs + ascofs + Point2(3, 0), text, font_color); + + ofs.y += h; + } + } else { + ofs.y += h; + } + + Ref<StyleBox> pg_bg = get_stylebox("bg", "ProgressBar"); + Ref<StyleBox> pg_fill = get_stylebox("fill", "ProgressBar"); + Rect2 pg_rect(ofs, Size2(w, h)); + + bool editable = true; + switch (type) { + case AnimationTreePlayer::NODE_ANIMATION: { + + Ref<Animation> anim = anim_tree->animation_node_get_animation(p_node); + String text; + if (anim_tree->animation_node_get_master_animation(p_node) != "") + text = anim_tree->animation_node_get_master_animation(p_node); + else if (anim.is_null()) + text = "load..."; + else + text = anim->get_name(); + + font->draw_halign(ci, ofs + ascofs, HALIGN_CENTER, w, text, font_color_title); + + } break; + case AnimationTreePlayer::NODE_ONESHOT: + case AnimationTreePlayer::NODE_MIX: + case AnimationTreePlayer::NODE_BLEND2: + case AnimationTreePlayer::NODE_BLEND3: + case AnimationTreePlayer::NODE_BLEND4: + case AnimationTreePlayer::NODE_TIMESCALE: + case AnimationTreePlayer::NODE_TRANSITION: { + + font->draw_halign(ci, ofs + ascofs, HALIGN_CENTER, w, "edit...", font_color_title); + } break; + default: editable = false; + } + + if (editable) { + + Ref<Texture> arrow = get_icon("GuiDropdown", "EditorIcons"); + Point2 arrow_ofs(w - arrow->get_width(), Math::floor((h - arrow->get_height()) / 2)); + arrow->draw(ci, ofs + arrow_ofs); + } +} + +AnimationTreePlayerEditor::ClickType AnimationTreePlayerEditor::_locate_click(const Point2 &p_click, StringName *p_node_id, int *p_slot_index) const { + + Ref<StyleBox> style = get_stylebox("panel", "PopupMenu"); + Ref<Font> font = get_font("font", "PopupMenu"); + + float h = (font->get_height() + get_constant("vseparation", "PopupMenu")); + + for (const List<StringName>::Element *E = order.back(); E; E = E->prev()) { + + StringName node = E->get(); + + AnimationTreePlayer::NodeType type = anim_tree->node_get_type(node); + + Point2 pos = anim_tree->node_get_position(node); + Size2 size = get_node_size(node); + + pos -= Point2(h_scroll->get_value(), v_scroll->get_value()); + + if (!Rect2(pos, size).has_point(p_click)) + continue; + + if (p_node_id) + *p_node_id = node; + + pos = p_click - pos; + + float y = pos.y - style->get_offset().height; + + if (y < 2 * h) + return CLICK_NODE; + y -= 2 * h; + + int inputs = anim_tree->node_get_input_count(node); + int count = MAX(inputs, 1); + + if (inputs == 0 || (pos.x > size.width / 2 && type != AnimationTreePlayer::NODE_OUTPUT)) { + + if (y < count * h) { + + if (p_slot_index) + *p_slot_index = 0; + return CLICK_OUTPUT_SLOT; + } + } + + for (int i = 0; i < count; i++) { + + if (y < h) { + if (p_slot_index) + *p_slot_index = i; + return CLICK_INPUT_SLOT; + } + y -= h; + } + + bool has_parameters = type != AnimationTreePlayer::NODE_OUTPUT && type != AnimationTreePlayer::NODE_TIMESEEK; + return has_parameters ? CLICK_PARAMETER : CLICK_NODE; + } + + return CLICK_NONE; +} + +Point2 AnimationTreePlayerEditor::_get_slot_pos(const StringName &p_node_id, bool p_input, int p_slot) { + + Ref<StyleBox> style = get_stylebox("panel", "PopupMenu"); + Ref<Font> font = get_font("font", "PopupMenu"); + Ref<Texture> slot_icon = get_icon("VisualShaderPort", "EditorIcons"); + + Size2 size = get_node_size(p_node_id); + Point2 pos = anim_tree->node_get_position(p_node_id); + + if (click_type == CLICK_NODE && click_node == p_node_id) { + + pos += click_motion - click_pos; + if (pos.x < 5) + pos.x = 5; + if (pos.y < 5) + pos.y = 5; + } + + pos -= Point2(h_scroll->get_value(), v_scroll->get_value()); + + float w = size.width - style->get_minimum_size().width; + float h = font->get_height() + get_constant("vseparation", "PopupMenu"); + + pos += style->get_offset(); + + pos.y += h * 2; + + pos.y += h * p_slot; + + pos += Point2(-slot_icon->get_width() / 2.0, h / 2.0).floor(); + + if (!p_input) { + pos.x += w + slot_icon->get_width(); + } + + return pos; +} + +void AnimationTreePlayerEditor::_gui_input(Ref<InputEvent> p_event) { + + Ref<InputEventMouseButton> mb = p_event; + + if (mb.is_valid()) { + + if (mb->is_pressed()) { + + if (mb->get_button_index() == 1) { + click_pos = Point2(mb->get_position().x, mb->get_position().y); + click_motion = click_pos; + click_type = _locate_click(click_pos, &click_node, &click_slot); + if (click_type != CLICK_NONE) { + + order.erase(click_node); + order.push_back(click_node); + update(); + } + + switch (click_type) { + case CLICK_INPUT_SLOT: { + click_pos = _get_slot_pos(click_node, true, click_slot); + } break; + case CLICK_OUTPUT_SLOT: { + click_pos = _get_slot_pos(click_node, false, click_slot); + } break; + case CLICK_PARAMETER: { + + edited_node = click_node; + renaming_edit = false; + _popup_edit_dialog(); + //open editor + //_node_edit_property(click_node); + } break; + default: {} + } + } + if (mb->get_button_index() == 2) { + + if (click_type != CLICK_NONE) { + click_type = CLICK_NONE; + update(); + } else { + // try to disconnect/remove + + Point2 rclick_pos = Point2(mb->get_position().x, mb->get_position().y); + rclick_type = _locate_click(rclick_pos, &rclick_node, &rclick_slot); + if (rclick_type == CLICK_INPUT_SLOT || rclick_type == CLICK_OUTPUT_SLOT) { + + node_popup->clear(); + node_popup->set_size(Size2(1, 1)); + node_popup->add_item(TTR("Disconnect"), NODE_DISCONNECT); + if (anim_tree->node_get_type(rclick_node) == AnimationTreePlayer::NODE_TRANSITION) { + node_popup->add_item(TTR("Add Input"), NODE_ADD_INPUT); + if (rclick_type == CLICK_INPUT_SLOT) { + if (anim_tree->transition_node_has_input_auto_advance(rclick_node, rclick_slot)) + node_popup->add_item(TTR("Clear Auto-Advance"), NODE_CLEAR_AUTOADVANCE); + else + node_popup->add_item(TTR("Set Auto-Advance"), NODE_SET_AUTOADVANCE); + node_popup->add_item(TTR("Delete Input"), NODE_DELETE_INPUT); + } + } + + node_popup->set_position(rclick_pos + get_global_position()); + node_popup->popup(); + } + + if (rclick_type == CLICK_NODE) { + node_popup->clear(); + node_popup->set_size(Size2(1, 1)); + node_popup->add_item(TTR("Rename"), NODE_RENAME); + node_popup->add_item(TTR("Remove"), NODE_ERASE); + if (anim_tree->node_get_type(rclick_node) == AnimationTreePlayer::NODE_TRANSITION) + node_popup->add_item(TTR("Add Input"), NODE_ADD_INPUT); + node_popup->set_position(rclick_pos + get_global_position()); + node_popup->popup(); + } + } + } + } else { + + if (mb->get_button_index() == 1 && click_type != CLICK_NONE) { + + switch (click_type) { + case CLICK_INPUT_SLOT: + case CLICK_OUTPUT_SLOT: { + + Point2 dst_click_pos = Point2(mb->get_position().x, mb->get_position().y); + StringName id; + int slot; + ClickType dst_click_type = _locate_click(dst_click_pos, &id, &slot); + + if (dst_click_type == CLICK_INPUT_SLOT && click_type == CLICK_OUTPUT_SLOT) { + + anim_tree->connect_nodes(click_node, id, slot); + } + if (click_type == CLICK_INPUT_SLOT && dst_click_type == CLICK_OUTPUT_SLOT) { + + anim_tree->connect_nodes(id, click_node, click_slot); + } + + } break; + case CLICK_NODE: { + Point2 new_pos = anim_tree->node_get_position(click_node) + (click_motion - click_pos); + if (new_pos.x < 5) + new_pos.x = 5; + if (new_pos.y < 5) + new_pos.y = 5; + anim_tree->node_set_position(click_node, new_pos); + + } break; + default: {} + } + + click_type = CLICK_NONE; + update(); + } + } + } + + Ref<InputEventMouseMotion> mm = p_event; + + if (mm.is_valid()) { + + if (mm->get_button_mask() & 1 && click_type != CLICK_NONE) { + + click_motion = Point2(mm->get_position().x, mm->get_position().y); + update(); + } + if ((mm->get_button_mask() & 4 || Input::get_singleton()->is_key_pressed(KEY_SPACE))) { + + h_scroll->set_value(h_scroll->get_value() - mm->get_relative().x); + v_scroll->set_value(v_scroll->get_value() - mm->get_relative().y); + update(); + } + } +} + +void AnimationTreePlayerEditor::_draw_cos_line(const Vector2 &p_from, const Vector2 &p_to, const Color &p_color) { + + static const int steps = 20; + + Rect2 r; + r.position = p_from; + r.expand_to(p_to); + Vector2 sign = Vector2((p_from.x < p_to.x) ? 1 : -1, (p_from.y < p_to.y) ? 1 : -1); + bool flip = sign.x * sign.y < 0; + + Vector2 prev; + for (int i = 0; i <= steps; i++) { + + float d = i / float(steps); + float c = -Math::cos(d * Math_PI) * 0.5 + 0.5; + if (flip) + c = 1.0 - c; + Vector2 p = r.position + Vector2(d * r.size.width, c * r.size.height); + + if (i > 0) { + + draw_line(prev, p, p_color, 2); + } + + prev = p; + } +} + +void AnimationTreePlayerEditor::_notification(int p_what) { + + switch (p_what) { + + case NOTIFICATION_ENTER_TREE: { + + play_button->set_icon(get_icon("Play", "EditorIcons")); + add_menu->set_icon(get_icon("Add", "EditorIcons")); + } break; + case NOTIFICATION_DRAW: { + + _update_scrollbars(); + //VisualServer::get_singleton()->canvas_item_add_rect(get_canvas_item(),Rect2(Point2(),get_size()),Color(0,0,0,1)); + get_stylebox("bg", "Tree")->draw(get_canvas_item(), Rect2(Point2(), get_size())); + + for (List<StringName>::Element *E = order.front(); E; E = E->next()) { + + _draw_node(E->get()); + } + + if (click_type == CLICK_INPUT_SLOT || click_type == CLICK_OUTPUT_SLOT) { + + _draw_cos_line(click_pos, click_motion, Color(0.5, 1, 0.5, 0.8)); + } + + List<AnimationTreePlayer::Connection> connections; + anim_tree->get_connection_list(&connections); + + for (List<AnimationTreePlayer::Connection>::Element *E = connections.front(); E; E = E->next()) { + + const AnimationTreePlayer::Connection &c = E->get(); + Point2 source = _get_slot_pos(c.src_node, false, 0); + Point2 dest = _get_slot_pos(c.dst_node, true, c.dst_input); + Color col = Color(1, 1, 0.5, 0.8); + /* + if (click_type==CLICK_NODE && click_node==c.src_node) { + + source+=click_motion-click_pos; + } + + if (click_type==CLICK_NODE && click_node==c.dst_node) { + + dest+=click_motion-click_pos; + }*/ + + _draw_cos_line(source, dest, col); + } + + switch (anim_tree->get_last_error()) { + + case AnimationTreePlayer::CONNECT_OK: { + + Ref<Font> f = get_font("font", "Label"); + f->draw(get_canvas_item(), Point2(5, 25 + f->get_ascent()), TTR("Animation tree is valid."), Color(0, 1, 0.6, 0.8)); + } break; + default: { + + Ref<Font> f = get_font("font", "Label"); + f->draw(get_canvas_item(), Point2(5, 25 + f->get_ascent()), TTR("Animation tree is invalid."), Color(1, 0.6, 0.0, 0.8)); + } break; + } + + } break; + } +} + +void AnimationTreePlayerEditor::_update_scrollbars() { + + Size2 size = get_size(); + Size2 hmin = h_scroll->get_combined_minimum_size(); + Size2 vmin = v_scroll->get_combined_minimum_size(); + + v_scroll->set_begin(Point2(size.width - vmin.width, 0)); + v_scroll->set_end(Point2(size.width, size.height)); + + h_scroll->set_begin(Point2(0, size.height - hmin.height)); + h_scroll->set_end(Point2(size.width - vmin.width, size.height)); + + Size2 min = _get_maximum_size(); + + if (min.height < size.height - hmin.height) { + + v_scroll->hide(); + offset.y = 0; + } else { + + v_scroll->show(); + v_scroll->set_max(min.height); + v_scroll->set_page(size.height - hmin.height); + offset.y = v_scroll->get_value(); + } + + if (min.width < size.width - vmin.width) { + + h_scroll->hide(); + offset.x = 0; + } else { + + h_scroll->show(); + h_scroll->set_max(min.width); + h_scroll->set_page(size.width - vmin.width); + offset.x = h_scroll->get_value(); + } +} + +void AnimationTreePlayerEditor::_scroll_moved(float) { + + offset.x = h_scroll->get_value(); + offset.y = v_scroll->get_value(); + update(); +} + +void AnimationTreePlayerEditor::_node_menu_item(int p_item) { + + switch (p_item) { + + case NODE_DISCONNECT: { + + if (rclick_type == CLICK_INPUT_SLOT) { + + anim_tree->disconnect_nodes(rclick_node, rclick_slot); + update(); + } + + if (rclick_type == CLICK_OUTPUT_SLOT) { + + List<AnimationTreePlayer::Connection> connections; + anim_tree->get_connection_list(&connections); + + for (List<AnimationTreePlayer::Connection>::Element *E = connections.front(); E; E = E->next()) { + + const AnimationTreePlayer::Connection &c = E->get(); + if (c.dst_node == rclick_node) { + + anim_tree->disconnect_nodes(c.dst_node, c.dst_input); + } + } + update(); + } + + } break; + case NODE_RENAME: { + + renaming_edit = true; + edited_node = rclick_node; + _popup_edit_dialog(); + + } break; + case NODE_ADD_INPUT: { + + anim_tree->transition_node_set_input_count(rclick_node, anim_tree->transition_node_get_input_count(rclick_node) + 1); + update(); + } break; + case NODE_DELETE_INPUT: { + + anim_tree->transition_node_delete_input(rclick_node, rclick_slot); + update(); + } break; + case NODE_SET_AUTOADVANCE: { + + anim_tree->transition_node_set_input_auto_advance(rclick_node, rclick_slot, true); + update(); + + } break; + case NODE_CLEAR_AUTOADVANCE: { + + anim_tree->transition_node_set_input_auto_advance(rclick_node, rclick_slot, false); + update(); + + } break; + + case NODE_ERASE: { + + if (rclick_node == "out") + break; + order.erase(rclick_node); + anim_tree->remove_node(rclick_node); + update(); + } break; + } +} + +StringName AnimationTreePlayerEditor::_add_node(int p_item) { + + static const char *bname[] = { + "out", + "anim", + "oneshot", + "mix", + "blend2", + "blend3", + "blend4", + "scale", + "seek", + "transition" + }; + + String name; + int idx = 1; + + while (true) { + + name = bname[p_item]; + if (idx > 1) + name += " " + itos(idx); + if (anim_tree->node_exists(name)) + idx++; + else + break; + } + + anim_tree->add_node((AnimationTreePlayer::NodeType)p_item, name); + anim_tree->node_set_position(name, Point2(last_x, last_y)); + order.push_back(name); + last_x += 10; + last_y += 10; + last_x = last_x % (int)get_size().width; + last_y = last_y % (int)get_size().height; + update(); + + return name; +}; + +void AnimationTreePlayerEditor::_file_dialog_selected(String p_path) { + + switch (file_op) { + + case MENU_IMPORT_ANIMATIONS: { + Vector<String> files = file_dialog->get_selected_files(); + + for (int i = 0; i < files.size(); i++) { + + StringName node = _add_node(AnimationTreePlayer::NODE_ANIMATION); + + RES anim = ResourceLoader::load(files[i]); + anim_tree->animation_node_set_animation(node, anim); + //anim_tree->node_set_name(node, files[i].get_file()); + }; + } break; + + default: + break; + }; +}; + +void AnimationTreePlayerEditor::_add_menu_item(int p_item) { + + if (p_item == MENU_GRAPH_CLEAR) { + + //clear + } else if (p_item == MENU_IMPORT_ANIMATIONS) { + + file_op = MENU_IMPORT_ANIMATIONS; + file_dialog->set_mode(EditorFileDialog::MODE_OPEN_FILE); + file_dialog->popup_centered_ratio(); + + } else { + + _add_node(p_item); + } +} + +Size2 AnimationTreePlayerEditor::get_minimum_size() const { + + return Size2(10, 200); +} + +void AnimationTreePlayerEditor::_find_paths_for_filter(const StringName &p_node, Set<String> &paths) { + + ERR_FAIL_COND(!anim_tree->node_exists(p_node)); + + for (int i = 0; i < anim_tree->node_get_input_count(p_node); i++) { + + StringName port = anim_tree->node_get_input_source(p_node, i); + if (port == StringName()) + continue; + _find_paths_for_filter(port, paths); + } + + if (anim_tree->node_get_type(p_node) == AnimationTreePlayer::NODE_ANIMATION) { + + Ref<Animation> anim = anim_tree->animation_node_get_animation(p_node); + if (anim.is_valid()) { + + for (int i = 0; i < anim->get_track_count(); i++) { + paths.insert(anim->track_get_path(i)); + } + } + } +} + +void AnimationTreePlayerEditor::_filter_edited() { + + TreeItem *ed = filter->get_edited(); + if (!ed) + return; + + if (anim_tree->node_get_type(edited_node) == AnimationTreePlayer::NODE_ONESHOT) { + anim_tree->oneshot_node_set_filter_path(edited_node, ed->get_metadata(0), ed->is_checked(0)); + } else if (anim_tree->node_get_type(edited_node) == AnimationTreePlayer::NODE_BLEND2) { + anim_tree->blend2_node_set_filter_path(edited_node, ed->get_metadata(0), ed->is_checked(0)); + } else if (anim_tree->node_get_type(edited_node) == AnimationTreePlayer::NODE_ANIMATION) { + anim_tree->animation_node_set_filter_path(edited_node, ed->get_metadata(0), ed->is_checked(0)); + } +} + +void AnimationTreePlayerEditor::_edit_filters() { + + filter_dialog->popup_centered_ratio(); + filter->clear(); + + Set<String> npb; + _find_paths_for_filter(edited_node, npb); + + TreeItem *root = filter->create_item(); + filter->set_hide_root(true); + Map<String, TreeItem *> pm; + + Node *base = anim_tree->get_node(anim_tree->get_base_path()); + + for (Set<String>::Element *E = npb.front(); E; E = E->next()) { + + TreeItem *parent = root; + String descr = E->get(); + if (base) { + NodePath np = E->get(); + + if (np.get_subname_count() == 1) { + Node *n = base->get_node(np); + Skeleton *s = Object::cast_to<Skeleton>(n); + if (s) { + + String skelbase = E->get().substr(0, E->get().find(":")); + + int bidx = s->find_bone(np.get_subname(0)); + + if (bidx != -1) { + int bparent = s->get_bone_parent(bidx); + // + if (bparent != -1) { + + String bpn = skelbase + ":" + s->get_bone_name(bparent); + if (pm.has(bpn)) { + parent = pm[bpn]; + descr = np.get_subname(0); + } + } else { + + if (pm.has(skelbase)) { + parent = pm[skelbase]; + } + } + } + } + } + } + + TreeItem *it = filter->create_item(parent); + it->set_cell_mode(0, TreeItem::CELL_MODE_CHECK); + it->set_text(0, descr); + it->set_metadata(0, NodePath(E->get())); + it->set_editable(0, true); + if (anim_tree->node_get_type(edited_node) == AnimationTreePlayer::NODE_ONESHOT) { + it->set_checked(0, anim_tree->oneshot_node_is_path_filtered(edited_node, E->get())); + } else if (anim_tree->node_get_type(edited_node) == AnimationTreePlayer::NODE_BLEND2) { + it->set_checked(0, anim_tree->blend2_node_is_path_filtered(edited_node, E->get())); + } else if (anim_tree->node_get_type(edited_node) == AnimationTreePlayer::NODE_ANIMATION) { + it->set_checked(0, anim_tree->animation_node_is_path_filtered(edited_node, E->get())); + } + pm[E->get()] = it; + } +} + +void AnimationTreePlayerEditor::_bind_methods() { + + ClassDB::bind_method("_add_menu_item", &AnimationTreePlayerEditor::_add_menu_item); + ClassDB::bind_method("_node_menu_item", &AnimationTreePlayerEditor::_node_menu_item); + ClassDB::bind_method("_gui_input", &AnimationTreePlayerEditor::_gui_input); + //ClassDB::bind_method( "_node_param_changed", &AnimationTreeEditor::_node_param_changed ); + ClassDB::bind_method("_scroll_moved", &AnimationTreePlayerEditor::_scroll_moved); + ClassDB::bind_method("_edit_dialog_changeds", &AnimationTreePlayerEditor::_edit_dialog_changeds); + ClassDB::bind_method("_edit_dialog_changede", &AnimationTreePlayerEditor::_edit_dialog_changede); + ClassDB::bind_method("_edit_dialog_changedf", &AnimationTreePlayerEditor::_edit_dialog_changedf); + ClassDB::bind_method("_edit_dialog_changed", &AnimationTreePlayerEditor::_edit_dialog_changed); + ClassDB::bind_method("_edit_dialog_animation_changed", &AnimationTreePlayerEditor::_edit_dialog_animation_changed); + ClassDB::bind_method("_edit_dialog_edit_animation", &AnimationTreePlayerEditor::_edit_dialog_edit_animation); + ClassDB::bind_method("_play_toggled", &AnimationTreePlayerEditor::_play_toggled); + ClassDB::bind_method("_edit_oneshot_start", &AnimationTreePlayerEditor::_edit_oneshot_start); + ClassDB::bind_method("_file_dialog_selected", &AnimationTreePlayerEditor::_file_dialog_selected); + ClassDB::bind_method("_master_anim_menu_item", &AnimationTreePlayerEditor::_master_anim_menu_item); + ClassDB::bind_method("_edit_filters", &AnimationTreePlayerEditor::_edit_filters); + ClassDB::bind_method("_filter_edited", &AnimationTreePlayerEditor::_filter_edited); +} + +AnimationTreePlayerEditor::AnimationTreePlayerEditor() { + + set_focus_mode(FOCUS_ALL); + + PopupMenu *p; + List<PropertyInfo> defaults; + + add_menu = memnew(MenuButton); + //add_menu->set_ + add_menu->set_position(Point2(0, 0)); + add_menu->set_size(Point2(25, 15)); + add_child(add_menu); + + p = add_menu->get_popup(); + p->add_item(TTR("Animation Node"), AnimationTreePlayer::NODE_ANIMATION); + p->add_item(TTR("OneShot Node"), AnimationTreePlayer::NODE_ONESHOT); + p->add_item(TTR("Mix Node"), AnimationTreePlayer::NODE_MIX); + p->add_item(TTR("Blend2 Node"), AnimationTreePlayer::NODE_BLEND2); + p->add_item(TTR("Blend3 Node"), AnimationTreePlayer::NODE_BLEND3); + p->add_item(TTR("Blend4 Node"), AnimationTreePlayer::NODE_BLEND4); + p->add_item(TTR("TimeScale Node"), AnimationTreePlayer::NODE_TIMESCALE); + p->add_item(TTR("TimeSeek Node"), AnimationTreePlayer::NODE_TIMESEEK); + p->add_item(TTR("Transition Node"), AnimationTreePlayer::NODE_TRANSITION); + p->add_separator(); + p->add_item(TTR("Import Animations..."), MENU_IMPORT_ANIMATIONS); // wtf + p->add_separator(); + p->add_item(TTR("Clear"), MENU_GRAPH_CLEAR); + + p->connect("id_pressed", this, "_add_menu_item"); + + play_button = memnew(Button); + play_button->set_position(Point2(25, 0)); + play_button->set_size(Point2(25, 15)); + add_child(play_button); + play_button->set_toggle_mode(true); + play_button->connect("pressed", this, "_play_toggled"); + + last_x = 50; + last_y = 50; + + property_editor = memnew(CustomPropertyEditor); + add_child(property_editor); + property_editor->connect("variant_changed", this, "_edit_dialog_animation_changed"); + property_editor->connect("resource_edit_request", this, "_edit_dialog_edit_animation"); + + h_scroll = memnew(HScrollBar); + v_scroll = memnew(VScrollBar); + + add_child(h_scroll); + add_child(v_scroll); + + h_scroll->connect("value_changed", this, "_scroll_moved"); + v_scroll->connect("value_changed", this, "_scroll_moved"); + + node_popup = memnew(PopupMenu); + add_child(node_popup); + node_popup->set_as_toplevel(true); + + master_anim_popup = memnew(PopupMenu); + add_child(master_anim_popup); + master_anim_popup->connect("id_pressed", this, "_master_anim_menu_item"); + + node_popup->connect("id_pressed", this, "_node_menu_item"); + + updating_edit = false; + + edit_dialog = memnew(PopupPanel); + //edit_dialog->get_ok()->hide(); + //edit_dialog->get_cancel()->hide(); + add_child(edit_dialog); + + edit_option = memnew(OptionButton); + edit_option->set_anchor(MARGIN_RIGHT, ANCHOR_END); + edit_option->set_margin(MARGIN_RIGHT, -10); + edit_dialog->add_child(edit_option); + edit_option->connect("item_selected", this, "_edit_dialog_changedf"); + edit_option->hide(); + + for (int i = 0; i < 2; i++) { + edit_scroll[i] = memnew(HSlider); + edit_scroll[i]->set_anchor(MARGIN_RIGHT, ANCHOR_END); + edit_scroll[i]->set_margin(MARGIN_RIGHT, -10); + edit_dialog->add_child(edit_scroll[i]); + edit_scroll[i]->hide(); + edit_scroll[i]->connect("value_changed", this, "_edit_dialog_changedf"); + } + for (int i = 0; i < 4; i++) { + edit_line[i] = memnew(LineEdit); + edit_line[i]->set_anchor(MARGIN_RIGHT, ANCHOR_END); + edit_line[i]->set_margin(MARGIN_RIGHT, -10); + edit_dialog->add_child(edit_line[i]); + edit_line[i]->hide(); + edit_line[i]->connect("text_changed", this, "_edit_dialog_changeds"); + edit_line[i]->connect("text_entered", this, "_edit_dialog_changede"); + edit_label[i] = memnew(Label); + edit_dialog->add_child(edit_label[i]); + edit_label[i]->hide(); + } + + edit_button = memnew(Button); + edit_button->set_anchor(MARGIN_RIGHT, ANCHOR_END); + edit_button->set_margin(MARGIN_RIGHT, -10); + edit_dialog->add_child(edit_button); + edit_button->hide(); + edit_button->connect("pressed", this, "_edit_oneshot_start"); + + edit_check = memnew(CheckButton); + edit_check->set_anchor(MARGIN_RIGHT, ANCHOR_END); + edit_check->set_margin(MARGIN_RIGHT, -10); + edit_dialog->add_child(edit_check); + edit_check->hide(); + edit_check->connect("pressed", this, "_edit_dialog_changed"); + + file_dialog = memnew(EditorFileDialog); + file_dialog->set_enable_multiple_selection(true); + file_dialog->set_current_dir(ProjectSettings::get_singleton()->get_resource_path()); + add_child(file_dialog); + file_dialog->connect("file_selected", this, "_file_dialog_selected"); + + filter_dialog = memnew(AcceptDialog); + filter_dialog->set_title(TTR("Edit Node Filters")); + add_child(filter_dialog); + + filter = memnew(Tree); + filter_dialog->add_child(filter); + //filter_dialog->set_child_rect(filter); + filter->connect("item_edited", this, "_filter_edited"); + + filter_button = memnew(Button); + filter_button->set_anchor(MARGIN_RIGHT, ANCHOR_END); + filter_button->set_margin(MARGIN_RIGHT, -10); + edit_dialog->add_child(filter_button); + filter_button->hide(); + filter_button->set_text(TTR("Filters...")); + filter_button->connect("pressed", this, "_edit_filters"); + + set_clip_contents(true); +} + +void AnimationTreePlayerEditorPlugin::edit(Object *p_object) { + + anim_tree_editor->edit(Object::cast_to<AnimationTreePlayer>(p_object)); +} + +bool AnimationTreePlayerEditorPlugin::handles(Object *p_object) const { + + return p_object->is_class("AnimationTreePlayer"); +} + +void AnimationTreePlayerEditorPlugin::make_visible(bool p_visible) { + + if (p_visible) { + //editor->hide_animation_player_editors(); + //editor->animation_panel_make_visible(true); + button->show(); + editor->make_bottom_panel_item_visible(anim_tree_editor); + anim_tree_editor->set_physics_process(true); + } else { + + if (anim_tree_editor->is_visible_in_tree()) + editor->hide_bottom_panel(); + button->hide(); + anim_tree_editor->set_physics_process(false); + } +} + +AnimationTreePlayerEditorPlugin::AnimationTreePlayerEditorPlugin(EditorNode *p_node) { + + editor = p_node; + anim_tree_editor = memnew(AnimationTreePlayerEditor); + anim_tree_editor->set_custom_minimum_size(Size2(0, 300)); + + button = editor->add_bottom_panel_item(TTR("AnimationTree"), anim_tree_editor); + button->hide(); +} + +AnimationTreePlayerEditorPlugin::~AnimationTreePlayerEditorPlugin() { +} diff --git a/editor/plugins/animation_tree_player_editor_plugin.h b/editor/plugins/animation_tree_player_editor_plugin.h new file mode 100644 index 0000000000..d1c5f395e4 --- /dev/null +++ b/editor/plugins/animation_tree_player_editor_plugin.h @@ -0,0 +1,187 @@ +/*************************************************************************/ +/* animation_tree_editor_plugin.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef ANIMATION_TREE_PLAYER_EDITOR_PLUGIN_H +#define ANIMATION_TREE_PLAYER_EDITOR_PLUGIN_H + +#include "editor/editor_node.h" +#include "editor/editor_plugin.h" +#include "editor/property_editor.h" +#include "scene/animation/animation_tree_player.h" +#include "scene/gui/button.h" +#include "scene/gui/popup.h" +#include "scene/gui/tree.h" +/** + @author Juan Linietsky <reduzio@gmail.com> +*/ + +class AnimationTreePlayerEditor : public Control { + + GDCLASS(AnimationTreePlayerEditor, Control); + + static const char *_node_type_names[]; + + enum ClickType { + CLICK_NONE, + CLICK_NAME, + CLICK_NODE, + CLICK_INPUT_SLOT, + CLICK_OUTPUT_SLOT, + CLICK_PARAMETER + }; + + enum { + + MENU_GRAPH_CLEAR = 100, + MENU_IMPORT_ANIMATIONS = 101, + NODE_DISCONNECT, + NODE_RENAME, + NODE_ERASE, + NODE_ADD_INPUT, + NODE_DELETE_INPUT, + NODE_SET_AUTOADVANCE, + NODE_CLEAR_AUTOADVANCE + }; + + bool renaming_edit; + StringName edited_node; + bool updating_edit; + Popup *edit_dialog; + HSlider *edit_scroll[2]; + LineEdit *edit_line[4]; + OptionButton *edit_option; + Label *edit_label[4]; + Button *edit_button; + Button *filter_button; + CheckButton *edit_check; + EditorFileDialog *file_dialog; + int file_op; + + void _popup_edit_dialog(); + + void _setup_edit_dialog(const StringName &p_node); + PopupMenu *master_anim_popup; + PopupMenu *node_popup; + PopupMenu *add_popup; + HScrollBar *h_scroll; + VScrollBar *v_scroll; + MenuButton *add_menu; + + CustomPropertyEditor *property_editor; + + AnimationTreePlayer *anim_tree; + List<StringName> order; + Set<StringName> active_nodes; + + int last_x, last_y; + + Point2 offset; + ClickType click_type; + Point2 click_pos; + StringName click_node; + int click_slot; + Point2 click_motion; + ClickType rclick_type; + StringName rclick_node; + int rclick_slot; + + Button *play_button; + + Size2 _get_maximum_size(); + Size2 get_node_size(const StringName &p_node) const; + void _draw_node(const StringName &p_node); + + AcceptDialog *filter_dialog; + Tree *filter; + + void _draw_cos_line(const Vector2 &p_from, const Vector2 &p_to, const Color &p_color); + void _update_scrollbars(); + void _scroll_moved(float); + void _play_toggled(); + /* + void _node_param_changed(); + void _node_add_callback(); + void _node_add(VisualServer::AnimationTreeNodeType p_type); + void _node_edit_property(const StringName& p_node); +*/ + + void _master_anim_menu_item(int p_item); + void _node_menu_item(int p_item); + void _add_menu_item(int p_item); + + void _filter_edited(); + void _find_paths_for_filter(const StringName &p_node, Set<String> &paths); + void _edit_filters(); + + void _edit_oneshot_start(); + void _edit_dialog_animation_changed(); + void _edit_dialog_edit_animation(); + void _edit_dialog_changeds(String); + void _edit_dialog_changede(String); + void _edit_dialog_changedf(float); + void _edit_dialog_changed(); + void _dialog_changed() const; + ClickType _locate_click(const Point2 &p_click, StringName *p_node_id, int *p_slot_index) const; + Point2 _get_slot_pos(const StringName &p_node_id, bool p_input, int p_slot); + + StringName _add_node(int p_item); + void _file_dialog_selected(String p_path); + +protected: + void _notification(int p_what); + void _gui_input(Ref<InputEvent> p_event); + static void _bind_methods(); + +public: + virtual Size2 get_minimum_size() const; + void edit(AnimationTreePlayer *p_anim_tree); + AnimationTreePlayerEditor(); +}; + +class AnimationTreePlayerEditorPlugin : public EditorPlugin { + + GDCLASS(AnimationTreePlayerEditorPlugin, EditorPlugin); + + AnimationTreePlayerEditor *anim_tree_editor; + EditorNode *editor; + Button *button; + +public: + virtual String get_name() const { return "AnimTree"; } + bool has_main_screen() const { return false; } + virtual void edit(Object *p_object); + virtual bool handles(Object *p_object) const; + virtual void make_visible(bool p_visible); + + AnimationTreePlayerEditorPlugin(EditorNode *p_node); + ~AnimationTreePlayerEditorPlugin(); +}; + +#endif // ANIMATION_TREE_EDITOR_PLUGIN_H diff --git a/editor/plugins/asset_library_editor_plugin.cpp b/editor/plugins/asset_library_editor_plugin.cpp index e98dfceb90..66770d98e5 100644 --- a/editor/plugins/asset_library_editor_plugin.cpp +++ b/editor/plugins/asset_library_editor_plugin.cpp @@ -384,14 +384,11 @@ void EditorAssetLibraryItemDownload::_http_download_completed(int p_status, int return; } - progress->set_max(download->get_body_size()); - progress->set_value(download->get_downloaded_bytes()); - install->set_disabled(false); + status->set_text(TTR("Success!")); + // Make the progress bar invisible but don't reflow other Controls around it + progress->set_modulate(Color(0, 0, 0, 0)); - progress->set_value(download->get_downloaded_bytes()); - - status->set_text(TTR("Success!") + " (" + String::humanize_size(download->get_downloaded_bytes()) + ")"); set_process(false); } @@ -413,25 +410,46 @@ void EditorAssetLibraryItemDownload::_notification(int p_what) { if (p_what == NOTIFICATION_PROCESS) { - progress->set_max(download->get_body_size()); - progress->set_value(download->get_downloaded_bytes()); + // Make the progress bar visible again when retrying the download + progress->set_modulate(Color(1, 1, 1, 1)); + + if (download->get_downloaded_bytes() > 0) { + progress->set_max(download->get_body_size()); + progress->set_value(download->get_downloaded_bytes()); + } int cstatus = download->get_http_client_status(); - if (cstatus == HTTPClient::STATUS_BODY) - status->set_text(TTR("Fetching:") + " " + String::humanize_size(download->get_downloaded_bytes())); + if (cstatus == HTTPClient::STATUS_BODY) { + if (download->get_body_size() > 0) { + status->set_text( + vformat( + TTR("Downloading (%s / %s)..."), + String::humanize_size(download->get_downloaded_bytes()), + String::humanize_size(download->get_body_size()))); + } else { + // Total file size is unknown, so it cannot be displayed + status->set_text(TTR("Downloading...")); + } + } if (cstatus != prev_status) { switch (cstatus) { case HTTPClient::STATUS_RESOLVING: { status->set_text(TTR("Resolving...")); + progress->set_max(1); + progress->set_value(0); } break; case HTTPClient::STATUS_CONNECTING: { status->set_text(TTR("Connecting...")); + progress->set_max(1); + progress->set_value(0); } break; case HTTPClient::STATUS_REQUESTING: { status->set_text(TTR("Requesting...")); + progress->set_max(1); + progress->set_value(0); } break; default: {} } @@ -527,7 +545,7 @@ EditorAssetLibraryItemDownload::EditorAssetLibraryItemDownload() { hb2->add_child(retry); hb2->add_child(install); - set_custom_minimum_size(Size2(250, 0)); + set_custom_minimum_size(Size2(310, 0)); download = memnew(HTTPRequest); add_child(download); @@ -554,6 +572,8 @@ void EditorAssetLibrary::_notification(int p_what) { error_tr->set_texture(get_icon("Error", "EditorIcons")); reverse->set_icon(get_icon("Sort", "EditorIcons")); + filter->set_right_icon(get_icon("Search", "EditorIcons")); + filter->set_clear_button_enabled(true); error_label->raise(); } break; @@ -604,6 +624,8 @@ void EditorAssetLibrary::_notification(int p_what) { library_scroll_bg->add_style_override("panel", get_stylebox("bg", "Tree")); error_tr->set_texture(get_icon("Error", "EditorIcons")); reverse->set_icon(get_icon("Sort", "EditorIcons")); + filter->set_right_icon(get_icon("Search", "EditorIcons")); + filter->set_clear_button_enabled(true); } break; } } diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp index 3738c472e7..72248b62a3 100644 --- a/editor/plugins/canvas_item_editor_plugin.cpp +++ b/editor/plugins/canvas_item_editor_plugin.cpp @@ -375,33 +375,24 @@ Rect2 CanvasItemEditor::_get_encompassing_rect_from_list(List<CanvasItem *> p_li // Handles the first element CanvasItem *canvas_item = p_list.front()->get(); - Rect2 rect; - if (canvas_item->_edit_use_rect()) { - rect = Rect2(canvas_item->get_global_transform_with_canvas().xform(canvas_item->_edit_get_rect().position + canvas_item->_edit_get_rect().size / 2), Size2()); - } else { - rect = Rect2(canvas_item->get_global_transform_with_canvas().xform(Point2()), Size2()); - } + Rect2 rect = Rect2(canvas_item->get_global_transform_with_canvas().xform(canvas_item->_edit_get_rect().position + canvas_item->_edit_get_rect().size / 2), Size2()); // Expand with the other ones for (List<CanvasItem *>::Element *E = p_list.front(); E; E = E->next()) { CanvasItem *canvas_item = E->get(); Transform2D xform = canvas_item->get_global_transform_with_canvas(); - if (canvas_item->_edit_use_rect()) { - Rect2 current_rect = canvas_item->_edit_get_rect(); - rect.expand_to(xform.xform(current_rect.position)); - rect.expand_to(xform.xform(current_rect.position + Vector2(current_rect.size.x, 0))); - rect.expand_to(xform.xform(current_rect.position + current_rect.size)); - rect.expand_to(xform.xform(current_rect.position + Vector2(0, current_rect.size.y))); - } else { - rect.expand_to(xform.xform(Point2())); - } + Rect2 current_rect = canvas_item->_edit_get_rect(); + rect.expand_to(xform.xform(current_rect.position)); + rect.expand_to(xform.xform(current_rect.position + Vector2(current_rect.size.x, 0))); + rect.expand_to(xform.xform(current_rect.position + current_rect.size)); + rect.expand_to(xform.xform(current_rect.position + Vector2(0, current_rect.size.y))); } return rect; } -void CanvasItemEditor::_expand_encompassing_rect_using_children(Rect2 &r_rect, const Node *p_node, bool &r_first, const Transform2D &p_parent_xform, const Transform2D &p_canvas_xform) { +void CanvasItemEditor::_expand_encompassing_rect_using_children(Rect2 &r_rect, const Node *p_node, bool &r_first, const Transform2D &p_parent_xform, const Transform2D &p_canvas_xform, bool include_locked_nodes) { if (!p_node) return; if (Object::cast_to<Viewport>(p_node)) @@ -409,12 +400,6 @@ void CanvasItemEditor::_expand_encompassing_rect_using_children(Rect2 &r_rect, c const CanvasItem *canvas_item = Object::cast_to<CanvasItem>(p_node); - /*bool inherited = p_node != get_tree()->get_edited_scene_root() && p_node->get_filename() != ""; - bool editable = !inherited || EditorNode::get_singleton()->get_edited_scene()->is_editable_instance(p_node); - bool lock_children = p_node->has_meta("_edit_group_") && p_node->get_meta("_edit_group_"); - - if (!lock_children && editable) {}*/ - for (int i = p_node->get_child_count() - 1; i >= 0; i--) { if (canvas_item && !canvas_item->is_set_as_toplevel()) { _expand_encompassing_rect_using_children(r_rect, p_node->get_child(i), r_first, p_parent_xform * canvas_item->get_transform(), p_canvas_xform); @@ -424,28 +409,17 @@ void CanvasItemEditor::_expand_encompassing_rect_using_children(Rect2 &r_rect, c } } - if (canvas_item && canvas_item->is_visible_in_tree() && !canvas_item->has_meta("_edit_lock_")) { + if (canvas_item && canvas_item->is_visible_in_tree() && (include_locked_nodes || !canvas_item->has_meta("_edit_lock_"))) { Transform2D xform = p_parent_xform * p_canvas_xform * canvas_item->get_transform(); - if (canvas_item->_edit_use_rect()) { - Rect2 rect = canvas_item->_edit_get_rect(); - if (r_first) { - r_rect = Rect2(xform.xform(rect.position + rect.size / 2), Size2()); - r_first = false; - } - if (r_rect.size != Size2()) { - r_rect.expand_to(xform.xform(rect.position)); - r_rect.expand_to(xform.xform(rect.position + Point2(rect.size.x, 0))); - r_rect.expand_to(xform.xform(rect.position + Point2(0, rect.size.y))); - r_rect.expand_to(xform.xform(rect.position + rect.size)); - } - } else { - if (r_first) { - r_rect = Rect2(xform.xform(Point2()), Size2()); - r_first = false; - } else { - r_rect.expand_to(xform.xform(Point2())); - } + Rect2 rect = canvas_item->_edit_get_rect(); + if (r_first) { + r_rect = Rect2(xform.xform(rect.position + rect.size / 2), Size2()); + r_first = false; } + r_rect.expand_to(xform.xform(rect.position)); + r_rect.expand_to(xform.xform(rect.position + Point2(rect.size.x, 0))); + r_rect.expand_to(xform.xform(rect.position + Point2(0, rect.size.y))); + r_rect.expand_to(xform.xform(rect.position + rect.size)); } } @@ -2302,14 +2276,14 @@ void CanvasItemEditor::_draw_grid() { real_grid_offset = grid_offset; } - const Color grid_minor_color = get_color("grid_minor_color", "Editor"); + const Color grid_color = EditorSettings::get_singleton()->get("editors/2d/grid_color"); if (grid_step.x != 0) { for (int i = 0; i < s.width; i++) { int cell = Math::fast_ftoi(Math::floor((xform.xform(Vector2(i, 0)).x - real_grid_offset.x) / (grid_step.x * Math::pow(2.0, grid_step_multiplier)))); if (i == 0) last_cell = cell; if (last_cell != cell) - viewport->draw_line(Point2(i, 0), Point2(i, s.height), grid_minor_color); + viewport->draw_line(Point2(i, 0), Point2(i, s.height), grid_color); last_cell = cell; } } @@ -2320,7 +2294,7 @@ void CanvasItemEditor::_draw_grid() { if (i == 0) last_cell = cell; if (last_cell != cell) - viewport->draw_line(Point2(0, i), Point2(s.width, i), grid_minor_color); + viewport->draw_line(Point2(0, i), Point2(s.width, i), grid_color); last_cell = cell; } } diff --git a/editor/plugins/canvas_item_editor_plugin.h b/editor/plugins/canvas_item_editor_plugin.h index adc4010f39..2c943385ad 100644 --- a/editor/plugins/canvas_item_editor_plugin.h +++ b/editor/plugins/canvas_item_editor_plugin.h @@ -390,7 +390,7 @@ class CanvasItemEditor : public VBoxContainer { List<CanvasItem *> _get_edited_canvas_items(bool retreive_locked = false, bool remove_canvas_item_if_parent_in_selection = true); Rect2 _get_encompassing_rect_from_list(List<CanvasItem *> p_list); - void _expand_encompassing_rect_using_children(Rect2 &p_rect, const Node *p_node, bool &r_first, const Transform2D &p_parent_xform = Transform2D(), const Transform2D &p_canvas_xform = Transform2D()); + void _expand_encompassing_rect_using_children(Rect2 &p_rect, const Node *p_node, bool &r_first, const Transform2D &p_parent_xform = Transform2D(), const Transform2D &p_canvas_xform = Transform2D(), bool include_locked_nodes = true); Rect2 _get_encompassing_rect(const Node *p_node); Object *_get_editor_data(Object *p_what); diff --git a/editor/plugins/curve_editor_plugin.cpp b/editor/plugins/curve_editor_plugin.cpp index 49c54ad67d..f5bdf77973 100644 --- a/editor/plugins/curve_editor_plugin.cpp +++ b/editor/plugins/curve_editor_plugin.cpp @@ -616,8 +616,8 @@ void CurveEditor::_draw() { Vector2 min_edge = get_world_pos(Vector2(0, view_size.y)); Vector2 max_edge = get_world_pos(Vector2(view_size.x, 0)); - const Color grid_color0 = get_color("grid_major_color", "Editor"); - const Color grid_color1 = get_color("grid_minor_color", "Editor"); + const Color grid_color0 = Color(1.0, 1.0, 1.0, 0.15); + const Color grid_color1 = Color(1.0, 1.0, 1.0, 0.07); draw_line(Vector2(min_edge.x, curve.get_min_value()), Vector2(max_edge.x, curve.get_min_value()), grid_color0); draw_line(Vector2(max_edge.x, curve.get_max_value()), Vector2(min_edge.x, curve.get_max_value()), grid_color0); draw_line(Vector2(0, min_edge.y), Vector2(0, max_edge.y), grid_color0); diff --git a/editor/plugins/particles_2d_editor_plugin.cpp b/editor/plugins/particles_2d_editor_plugin.cpp index c2b17189ef..b50e0dfe88 100644 --- a/editor/plugins/particles_2d_editor_plugin.cpp +++ b/editor/plugins/particles_2d_editor_plugin.cpp @@ -68,6 +68,11 @@ void Particles2DEditorPlugin::_menu_callback(int p_idx) { switch (p_idx) { case MENU_GENERATE_VISIBILITY_RECT: { + float gen_time = particles->get_lifetime(); + if (gen_time < 1.0) + generate_seconds->set_value(1.0); + else + generate_seconds->set_value(trunc(gen_time) + 1.0); generate_aabb->popup_centered_minsize(); } break; case MENU_LOAD_EMISSION_MASK: { @@ -90,6 +95,12 @@ void Particles2DEditorPlugin::_generate_visibility_rect() { EditorProgress ep("gen_aabb", TTR("Generating AABB"), int(time)); + bool was_emitting = particles->is_emitting(); + if (!was_emitting) { + particles->set_emitting(true); + OS::get_singleton()->delay_usec(1000); + } + Rect2 rect; while (running < time) { @@ -106,6 +117,10 @@ void Particles2DEditorPlugin::_generate_visibility_rect() { running += (OS::get_singleton()->get_ticks_usec() - ticks) / 1000000.0; } + if (!was_emitting) { + particles->set_emitting(false); + } + particles->set_visibility_rect(rect); } diff --git a/editor/plugins/particles_editor_plugin.cpp b/editor/plugins/particles_editor_plugin.cpp index 1f5a4a8a36..6a99dcb9a5 100644 --- a/editor/plugins/particles_editor_plugin.cpp +++ b/editor/plugins/particles_editor_plugin.cpp @@ -270,6 +270,12 @@ void ParticlesEditor::_menu_option(int p_option) { switch (p_option) { case MENU_OPTION_GENERATE_AABB: { + float gen_time = node->get_lifetime(); + + if (gen_time < 1.0) + generate_seconds->set_value(1.0); + else + generate_seconds->set_value(trunc(gen_time) + 1.0); generate_aabb->popup_centered_minsize(); } break; case MENU_OPTION_CREATE_EMISSION_VOLUME_FROM_MESH: { @@ -323,7 +329,14 @@ void ParticlesEditor::_generate_aabb() { EditorProgress ep("gen_aabb", TTR("Generating AABB"), int(time)); + bool was_emitting = node->is_emitting(); + if (!was_emitting) { + node->set_emitting(true); + OS::get_singleton()->delay_usec(1000); + } + AABB rect; + while (running < time) { uint64_t ticks = OS::get_singleton()->get_ticks_usec(); @@ -339,6 +352,10 @@ void ParticlesEditor::_generate_aabb() { running += (OS::get_singleton()->get_ticks_usec() - ticks) / 1000000.0; } + if (!was_emitting) { + node->set_emitting(false); + } + node->set_visibility_aabb(rect); } diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp index af242e2d98..1bb7c98114 100644 --- a/editor/plugins/script_editor_plugin.cpp +++ b/editor/plugins/script_editor_plugin.cpp @@ -219,6 +219,9 @@ void ScriptEditorQuickOpen::_notification(int p_what) { case NOTIFICATION_ENTER_TREE: { connect("confirmed", this, "_confirmed"); + + search_box->set_right_icon(get_icon("Search", "EditorIcons")); + search_box->set_clear_button_enabled(true); } break; } } @@ -839,6 +842,19 @@ bool ScriptEditor::_test_script_times_on_disk(RES p_for_script) { void ScriptEditor::_file_dialog_action(String p_file) { switch (file_dialog_option) { + case FILE_NEW_TEXTFILE: { + Error err; + FileAccess *file = FileAccess::open(p_file, FileAccess::WRITE, &err); + if (err) { + memdelete(file); + editor->show_warning(TTR("Error writing TextFile:") + "\n" + p_file, TTR("Error!")); + break; + } + file->close(); + memdelete(file); + + // fallthrough to open the file. + } case FILE_OPEN: { List<String> extensions; @@ -867,7 +883,7 @@ void ScriptEditor::_file_dialog_action(String p_file) { file_dialog_option = -1; return; } - } + } break; case FILE_SAVE_AS: { ScriptEditorBase *current = _get_current_editor(); @@ -926,6 +942,15 @@ void ScriptEditor::_menu_option(int p_option) { script_create_dialog->config("Node", ".gd"); script_create_dialog->popup_centered(Size2(300, 300) * EDSCALE); } break; + case FILE_NEW_TEXTFILE: { + file_dialog->set_mode(EditorFileDialog::MODE_SAVE_FILE); + file_dialog->set_access(EditorFileDialog::ACCESS_FILESYSTEM); + file_dialog_option = FILE_NEW_TEXTFILE; + + file_dialog->clear_filters(); + file_dialog->popup_centered_ratio(); + file_dialog->set_title(TTR("New TextFile...")); + } break; case FILE_OPEN: { file_dialog->set_mode(EditorFileDialog::MODE_OPEN_FILE); file_dialog->set_access(EditorFileDialog::ACCESS_FILESYSTEM); @@ -1934,7 +1959,7 @@ bool ScriptEditor::edit(const RES &p_resource, int p_line, int p_col, bool p_gra if (!se) continue; - if (se->get_edited_resource() == p_resource) { + if ((script != NULL && se->get_edited_resource() == p_resource) || se->get_edited_resource()->get_path() == p_resource->get_path()) { if (should_open) { if (tab_container->get_current_tab() != i) { @@ -2384,9 +2409,23 @@ void ScriptEditor::_unhandled_input(const Ref<InputEvent> &p_event) { void ScriptEditor::_script_list_gui_input(const Ref<InputEvent> &ev) { Ref<InputEventMouseButton> mb = ev; - if (mb.is_valid() && mb->get_button_index() == BUTTON_RIGHT && mb->is_pressed()) { + if (mb.is_valid() && mb->is_pressed()) { + switch (mb->get_button_index()) { + + case BUTTON_MIDDLE: { + // Right-click selects automatically; middle-click does not. + int idx = script_list->get_item_at_position(mb->get_position(), true); + if (idx >= 0) { + script_list->select(idx); + _script_selected(idx); + _menu_option(FILE_CLOSE); + } + } break; - _make_script_list_context_menu(); + case BUTTON_RIGHT: { + _make_script_list_context_menu(); + } break; + } } } @@ -2894,7 +2933,7 @@ ScriptEditor::ScriptEditor(EditorNode *p_editor) { script_list->set_v_size_flags(SIZE_EXPAND_FILL); script_split->set_split_offset(140); _sort_list_on_update = true; - script_list->connect("gui_input", this, "_script_list_gui_input"); + script_list->connect("gui_input", this, "_script_list_gui_input", varray(), CONNECT_DEFERRED); script_list->set_allow_rmb_select(true); script_list->set_drag_forwarding(this); @@ -2957,7 +2996,8 @@ ScriptEditor::ScriptEditor(EditorNode *p_editor) { menu_hb->add_child(file_menu); file_menu->set_text(TTR("File")); file_menu->get_popup()->set_hide_on_window_lose_focus(true); - file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/new", TTR("New")), FILE_NEW); + file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/new", TTR("New Script")), FILE_NEW); + file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/new_textfile", TTR("New TextFile")), FILE_NEW_TEXTFILE); file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/open", TTR("Open")), FILE_OPEN); file_menu->get_popup()->add_submenu_item(TTR("Open Recent"), "RecentScripts", FILE_OPEN_RECENT); diff --git a/editor/plugins/script_editor_plugin.h b/editor/plugins/script_editor_plugin.h index 186c80a5f9..737f17358b 100644 --- a/editor/plugins/script_editor_plugin.h +++ b/editor/plugins/script_editor_plugin.h @@ -130,6 +130,7 @@ class ScriptEditor : public PanelContainer { EditorNode *editor; enum { FILE_NEW, + FILE_NEW_TEXTFILE, FILE_OPEN, FILE_OPEN_RECENT, FILE_SAVE, diff --git a/editor/plugins/spatial_editor_plugin.cpp b/editor/plugins/spatial_editor_plugin.cpp index 8871d8ac7e..7ad117deb6 100644 --- a/editor/plugins/spatial_editor_plugin.cpp +++ b/editor/plugins/spatial_editor_plugin.cpp @@ -2081,6 +2081,15 @@ void SpatialEditorViewport::set_message(String p_message, float p_time) { message_time = p_time; } +void SpatialEditorPlugin::edited_scene_changed() { + for (int i = 0; i < SpatialEditor::VIEWPORTS_COUNT; i++) { + SpatialEditorViewport *viewport = SpatialEditor::get_singleton()->get_editor_viewport(i); + if (viewport->is_visible()) { + viewport->notification(Control::NOTIFICATION_VISIBILITY_CHANGED); + } + } +} + void SpatialEditorViewport::_notification(int p_what) { if (p_what == NOTIFICATION_VISIBILITY_CHANGED) { @@ -2824,7 +2833,7 @@ void SpatialEditorViewport::update_transform_gizmo_view() { dd = 0.0001; float gsize = EditorSettings::get_singleton()->get("editors/3d/manipulator_gizmo_size"); - gizmo_scale = (gsize / Math::abs(dd)); + gizmo_scale = (gsize / Math::abs(dd)) * MAX(1, EDSCALE) / viewport_container->get_stretch_shrink(); Vector3 scale = Vector3(1, 1, 1) * gizmo_scale; xform.basis.scale(scale); @@ -3346,7 +3355,7 @@ void SpatialEditorViewport::drop_data_fw(const Point2 &p_point, const Variant &p if (root_node) { list.push_back(root_node); } else { - accept->get_ok()->set_text(TTR("OK :(")); + accept->get_ok()->set_text(TTR("OK")); accept->set_text(TTR("No parent to instance a child at.")); accept->popup_centered_minsize(); _remove_preview(); @@ -3386,6 +3395,7 @@ SpatialEditorViewport::SpatialEditorViewport(SpatialEditor *p_spatial_editor, Ed clicked = 0; clicked_includes_current = false; orthogonal = false; + lock_rotation = false; message_time = 0; zoom_indicator_delay = 0.0; @@ -3849,10 +3859,6 @@ void SpatialEditor::select_gizmo_highlight_axis(int p_axis) { } } -int SpatialEditor::get_skeleton_visibility_state() const { - return view_menu->get_popup()->get_item_state(view_menu->get_popup()->get_item_index(MENU_VISIBILITY_SKELETON)); -} - void SpatialEditor::update_transform_gizmo() { List<Node *> &selection = editor_selection->get_selected_node_list(); @@ -4008,9 +4014,9 @@ Dictionary SpatialEditor::get_state() const { Dictionary gizmos_status; for (int i = 0; i < gizmo_plugins.size(); i++) { if (!gizmo_plugins[i]->can_be_hidden()) continue; - bool checked = gizmos_menu->get_popup()->is_item_checked(gizmos_menu->get_popup()->get_item_index(i)); + int state = gizmos_menu->get_item_state(gizmos_menu->get_item_index(i)); String name = gizmo_plugins[i]->get_name(); - gizmos_status[name] = checked; + gizmos_status[name] = state; } d["gizmos_status"] = gizmos_status; @@ -4096,14 +4102,29 @@ void SpatialEditor::set_state(const Dictionary &p_state) { for (int j = 0; j < gizmo_plugins.size(); ++j) { if (!gizmo_plugins[j]->can_be_hidden()) continue; - bool checked = true; + int state = EditorSpatialGizmoPlugin::ON_TOP; for (uint32_t i = 0; i < keys.size(); i++) { if (gizmo_plugins.write[j]->get_name() == keys[i]) { - checked = gizmos_status[keys[i]]; + state = gizmos_status[keys[i]]; } } - gizmos_menu->get_popup()->set_item_checked(gizmos_menu->get_popup()->get_item_index(j), checked); - gizmo_plugins.write[j]->set_hidden(!checked); + + const int idx = gizmos_menu->get_item_index(j); + + gizmos_menu->set_item_multistate(idx, state); + gizmo_plugins.write[j]->set_state(state); + + switch (state) { + case EditorSpatialGizmoPlugin::ON_TOP: + gizmos_menu->set_item_icon(idx, gizmos_menu->get_icon("visibility_visible")); + break; + case EditorSpatialGizmoPlugin::VISIBLE: + gizmos_menu->set_item_icon(idx, gizmos_menu->get_icon("visibility_xray")); + break; + case EditorSpatialGizmoPlugin::HIDDEN: + gizmos_menu->set_item_icon(idx, gizmos_menu->get_icon("visibility_hidden")); + break; + } } } } @@ -4200,12 +4221,27 @@ void SpatialEditor::_menu_item_toggled(bool pressed, int p_option) { } void SpatialEditor::_menu_gizmo_toggled(int p_option) { - bool is_checked = gizmos_menu->get_popup()->is_item_checked(gizmos_menu->get_popup()->get_item_index(p_option)); - is_checked = !is_checked; - gizmo_plugins.write[p_option]->set_hidden(!is_checked); + const int idx = gizmos_menu->get_item_index(p_option); + gizmos_menu->toggle_item_multistate(idx); - gizmos_menu->get_popup()->set_item_checked(gizmos_menu->get_popup()->get_item_index(p_option), is_checked); + // Change icon + const int state = gizmos_menu->get_item_state(idx); + switch (state) { + case EditorSpatialGizmoPlugin::ON_TOP: + gizmos_menu->set_item_icon(idx, view_menu->get_popup()->get_icon("visibility_visible")); + break; + case EditorSpatialGizmoPlugin::VISIBLE: + gizmos_menu->set_item_icon(idx, view_menu->get_popup()->get_icon("visibility_xray")); + break; + case EditorSpatialGizmoPlugin::HIDDEN: + gizmos_menu->set_item_icon(idx, view_menu->get_popup()->get_icon("visibility_hidden")); + break; + } + + gizmo_plugins.write[p_option]->set_state(state); + + update_all_gizmos(); } void SpatialEditor::_menu_item_pressed(int p_option) { @@ -4316,10 +4352,13 @@ void SpatialEditor::_menu_item_pressed(int p_option) { bool is_checked = view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(p_option)); - is_checked = !is_checked; - VisualServer::get_singleton()->instance_set_visible(origin_instance, is_checked); + origin_enabled = !is_checked; + VisualServer::get_singleton()->instance_set_visible(origin_instance, origin_enabled); + // Update the grid since its appearance depends on whether the origin is enabled + _finish_grid(); + _init_grid(); - view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(p_option), is_checked); + view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(p_option), origin_enabled); } break; case MENU_VIEW_GRID: { @@ -4382,28 +4421,6 @@ void SpatialEditor::_menu_item_pressed(int p_option) { _refresh_menu_icons(); } break; - case MENU_VISIBILITY_SKELETON: { - - const int idx = view_menu->get_popup()->get_item_index(MENU_VISIBILITY_SKELETON); - view_menu->get_popup()->toggle_item_multistate(idx); - - // Change icon - const int state = view_menu->get_popup()->get_item_state(idx); - switch (state) { - case 0: - view_menu->get_popup()->set_item_icon(idx, view_menu->get_popup()->get_icon("visibility_hidden")); - break; - case 1: - view_menu->get_popup()->set_item_icon(idx, view_menu->get_popup()->get_icon("visibility_visible")); - break; - case 2: - view_menu->get_popup()->set_item_icon(idx, view_menu->get_popup()->get_icon("visibility_xray")); - break; - } - - update_all_gizmos(); - - } break; } } @@ -4513,9 +4530,9 @@ void SpatialEditor::_init_indicators() { nivec * 0.0 + ivec * (GIZMO_ARROW_OFFSET + GIZMO_ARROW_SIZE), }; - int arrow_sides = 6; + int arrow_sides = 16; - for (int k = 0; k < 6; k++) { + for (int k = 0; k < arrow_sides; k++) { Basis ma(ivec, Math_PI * 2 * float(k) / arrow_sides); Basis mb(ivec, Math_PI * 2 * float(k + 1) / arrow_sides); @@ -4599,10 +4616,10 @@ void SpatialEditor::_init_indicators() { ivec * 0.02 + ivec2 * 0.02 + ivec2 * GIZMO_CIRCLE_SIZE, }; - for (int k = 0; k < 32; k++) { + for (int k = 0; k < 64; k++) { - Basis ma(ivec, Math_PI * 2 * float(k) / 32); - Basis mb(ivec, Math_PI * 2 * float(k + 1) / 32); + Basis ma(ivec, Math_PI * 2 * float(k) / 64); + Basis mb(ivec, Math_PI * 2 * float(k + 1) / 64); for (int j = 0; j < 4; j++) { @@ -4729,14 +4746,13 @@ struct _GizmoPluginComparator { void SpatialEditor::_init_gizmos_menu() { _register_all_gizmos(); - PopupMenu *p = gizmos_menu->get_popup(); - gizmo_plugins.sort_custom<_GizmoPluginComparator>(); for (int i = 0; i < gizmo_plugins.size(); ++i) { if (!gizmo_plugins[i]->can_be_hidden()) continue; String plugin_name = gizmo_plugins[i]->get_name(); - p->add_check_item(TTR(plugin_name), i); + gizmos_menu->add_multistate_item(TTR(plugin_name), 3, EditorSpatialGizmoPlugin::ON_TOP, i); + gizmos_menu->set_item_icon(gizmos_menu->get_item_index(i), gizmos_menu->get_icon("visibility_visible")); } } @@ -4765,7 +4781,11 @@ void SpatialEditor::_init_grid() { Vector3 p2_dest = p2 * (-axis_n1 + axis_n2); Color line_color = secondary_grid_color; - if (j % primary_grid_steps == 0) { + if (origin_enabled && j == 0) { + // Don't draw the center lines of the grid if the origin is enabled + // The origin would overlap the grid lines in this case, causing flickering + continue; + } else if (j % primary_grid_steps == 0) { line_color = primary_grid_color; } @@ -5020,7 +5040,6 @@ void SpatialEditor::_notification(int p_what) { view_menu->get_popup()->set_item_icon(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_3_VIEWPORTS), get_icon("Panels3", "EditorIcons")); view_menu->get_popup()->set_item_icon(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_3_VIEWPORTS_ALT), get_icon("Panels3Alt", "EditorIcons")); view_menu->get_popup()->set_item_icon(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_4_VIEWPORTS), get_icon("Panels4", "EditorIcons")); - view_menu->get_popup()->set_item_icon(view_menu->get_popup()->get_item_index(MENU_VISIBILITY_SKELETON), view_menu->get_popup()->get_icon("visibility_visible")); _menu_item_pressed(MENU_VIEW_USE_1_VIEWPORT); @@ -5175,9 +5194,10 @@ void SpatialEditor::_register_all_gizmos() { register_gizmo_plugin(Ref<MeshInstanceSpatialGizmoPlugin>(memnew(MeshInstanceSpatialGizmoPlugin))); register_gizmo_plugin(Ref<SoftBodySpatialGizmoPlugin>(memnew(SoftBodySpatialGizmoPlugin))); register_gizmo_plugin(Ref<Sprite3DSpatialGizmoPlugin>(memnew(Sprite3DSpatialGizmoPlugin))); - register_gizmo_plugin(Ref<Position3DSpatialGizmoPlugin>(memnew(Position3DSpatialGizmoPlugin))); register_gizmo_plugin(Ref<SkeletonSpatialGizmoPlugin>(memnew(SkeletonSpatialGizmoPlugin))); + register_gizmo_plugin(Ref<Position3DSpatialGizmoPlugin>(memnew(Position3DSpatialGizmoPlugin))); register_gizmo_plugin(Ref<RayCastSpatialGizmoPlugin>(memnew(RayCastSpatialGizmoPlugin))); + register_gizmo_plugin(Ref<SpringArmSpatialGizmoPlugin>(memnew(SpringArmSpatialGizmoPlugin))); register_gizmo_plugin(Ref<VehicleWheelSpatialGizmoPlugin>(memnew(VehicleWheelSpatialGizmoPlugin))); register_gizmo_plugin(Ref<VisibilityNotifierGizmoPlugin>(memnew(VisibilityNotifierGizmoPlugin))); register_gizmo_plugin(Ref<ParticlesGizmoPlugin>(memnew(ParticlesGizmoPlugin))); @@ -5397,24 +5417,25 @@ SpatialEditor::SpatialEditor(EditorNode *p_editor) { p->add_radio_check_shortcut(ED_SHORTCUT("spatial_editor/4_viewports", TTR("4 Viewports"), KEY_MASK_CMD + KEY_4), MENU_VIEW_USE_4_VIEWPORTS); p->add_separator(); + p->add_submenu_item(TTR("Gizmos"), "GizmosMenu"); + + p->add_separator(); + p->add_check_shortcut(ED_SHORTCUT("spatial_editor/view_origin", TTR("View Origin")), MENU_VIEW_ORIGIN); p->add_check_shortcut(ED_SHORTCUT("spatial_editor/view_grid", TTR("View Grid")), MENU_VIEW_GRID); p->add_separator(); p->add_shortcut(ED_SHORTCUT("spatial_editor/settings", TTR("Settings")), MENU_VIEW_CAMERA_SETTINGS); - p->add_separator(); - p->add_multistate_item(TTR("Skeleton Gizmo visibility"), 3, 1, MENU_VISIBILITY_SKELETON); - p->set_item_checked(p->get_item_index(MENU_VIEW_ORIGIN), true); p->set_item_checked(p->get_item_index(MENU_VIEW_GRID), true); p->connect("id_pressed", this, "_menu_item_pressed"); - gizmos_menu = memnew(MenuButton); - gizmos_menu->set_text(TTR("Gizmos")); - hbc_menu->add_child(gizmos_menu); - gizmos_menu->get_popup()->set_hide_on_checkable_item_selection(false); - gizmos_menu->get_popup()->connect("id_pressed", this, "_menu_gizmo_toggled"); + gizmos_menu = memnew(PopupMenu); + p->add_child(gizmos_menu); + gizmos_menu->set_name("GizmosMenu"); + gizmos_menu->set_hide_on_checkable_item_selection(false); + gizmos_menu->connect("id_pressed", this, "_menu_gizmo_toggled"); /* REST OF MENU */ @@ -5662,6 +5683,7 @@ void EditorSpatialGizmoPlugin::create_material(const String &p_name, const Color material->set_albedo(color); material->set_flag(SpatialMaterial::FLAG_UNSHADED, true); material->set_feature(SpatialMaterial::FEATURE_TRANSPARENT, true); + material->set_render_priority(SpatialMaterial::RENDER_PRIORITY_MIN + 1); if (p_use_vertex_color) { material->set_flag(SpatialMaterial::FLAG_ALBEDO_FROM_VERTEX_COLOR, true); @@ -5709,6 +5731,7 @@ void EditorSpatialGizmoPlugin::create_icon_material(const String &p_name, const icon->set_texture(SpatialMaterial::TEXTURE_ALBEDO, p_texture); icon->set_flag(SpatialMaterial::FLAG_FIXED_SIZE, true); icon->set_billboard_mode(SpatialMaterial::BILLBOARD_ENABLED); + icon->set_render_priority(SpatialMaterial::RENDER_PRIORITY_MIN); if (p_on_top && selected) { icon->set_on_top_of_alpha(); @@ -5755,7 +5778,16 @@ Ref<SpatialMaterial> EditorSpatialGizmoPlugin::get_material(const String &p_name if (p_gizmo == NULL) return materials[p_name][0]; int index = (p_gizmo->is_selected() ? 1 : 0) + (p_gizmo->is_editable() ? 2 : 0); - return materials[p_name][index]; + + Ref<SpatialMaterial> mat = materials[p_name][index]; + + if (current_state == ON_TOP && p_gizmo->is_selected()) { + mat->set_flag(SpatialMaterial::FLAG_DISABLE_DEPTH_TEST, true); + } else { + mat->set_flag(SpatialMaterial::FLAG_DISABLE_DEPTH_TEST, false); + } + + return mat; } Ref<EditorSpatialGizmo> EditorSpatialGizmoPlugin::get_gizmo(Spatial *p_spatial) { @@ -5766,7 +5798,7 @@ Ref<EditorSpatialGizmo> EditorSpatialGizmoPlugin::get_gizmo(Spatial *p_spatial) ref->set_plugin(this); ref->set_spatial_node(p_spatial); - ref->set_hidden(hidden); + ref->set_hidden(current_state == HIDDEN); current_gizmos.push_back(ref.ptr()); return ref; @@ -5791,10 +5823,10 @@ bool EditorSpatialGizmoPlugin::is_selectable_when_hidden() const { return false; } -void EditorSpatialGizmoPlugin::set_hidden(bool p_hidden) { - hidden = p_hidden; +void EditorSpatialGizmoPlugin::set_state(int p_state) { + current_state = p_state; for (int i = 0; i < current_gizmos.size(); ++i) { - current_gizmos[i]->set_hidden(hidden); + current_gizmos[i]->set_hidden(current_state == HIDDEN); } } @@ -5803,7 +5835,7 @@ void EditorSpatialGizmoPlugin::unregister_gizmo(EditorSpatialGizmo *p_gizmo) { } EditorSpatialGizmoPlugin::EditorSpatialGizmoPlugin() { - hidden = false; + current_state = ON_TOP; } EditorSpatialGizmoPlugin::~EditorSpatialGizmoPlugin() { diff --git a/editor/plugins/spatial_editor_plugin.h b/editor/plugins/spatial_editor_plugin.h index 42e6a24bc5..5850c0dbf1 100644 --- a/editor/plugins/spatial_editor_plugin.h +++ b/editor/plugins/spatial_editor_plugin.h @@ -508,6 +508,7 @@ private: RID origin; RID origin_instance; + bool origin_enabled; RID grid[3]; RID grid_instance[3]; bool grid_visible[3]; //currently visible @@ -560,10 +561,10 @@ private: MENU_VIEW_USE_4_VIEWPORTS, MENU_VIEW_ORIGIN, MENU_VIEW_GRID, + MENU_VIEW_GIZMOS_3D_ICONS, MENU_VIEW_CAMERA_SETTINGS, MENU_LOCK_SELECTED, MENU_UNLOCK_SELECTED, - MENU_VISIBILITY_SKELETON, MENU_SNAP_TO_FLOOR }; @@ -571,7 +572,7 @@ private: Button *tool_option_button[TOOL_OPT_MAX]; MenuButton *transform_menu; - MenuButton *gizmos_menu; + PopupMenu *gizmos_menu; MenuButton *view_menu; ToolButton *lock_button; @@ -675,8 +676,6 @@ public: Ref<ArrayMesh> get_scale_gizmo(int idx) const { return scale_gizmo[idx]; } Ref<ArrayMesh> get_scale_plane_gizmo(int idx) const { return scale_plane_gizmo[idx]; } - int get_skeleton_visibility_state() const; - void update_transform_gizmo(); void update_all_gizmos(); void snap_selected_nodes_to_floor(); @@ -743,6 +742,8 @@ public: virtual void set_state(const Dictionary &p_state); virtual void clear() { spatial_editor->clear(); } + virtual void edited_scene_changed(); + SpatialEditorPlugin(EditorNode *p_node); ~SpatialEditorPlugin(); }; @@ -751,7 +752,13 @@ class EditorSpatialGizmoPlugin : public Resource { GDCLASS(EditorSpatialGizmoPlugin, Resource); - bool hidden; +public: + static const int ON_TOP = 0; + static const int VISIBLE = 1; + static const int HIDDEN = 2; + +private: + int current_state; List<EditorSpatialGizmo *> current_gizmos; HashMap<String, Vector<Ref<SpatialMaterial> > > materials; @@ -779,7 +786,7 @@ public: virtual bool is_gizmo_handle_highlighted(const EditorSpatialGizmo *p_gizmo, int idx) const { return false; } Ref<EditorSpatialGizmo> get_gizmo(Spatial *p_spatial); - void set_hidden(bool p_hidden); + void set_state(int p_state); void unregister_gizmo(EditorSpatialGizmo *p_gizmo); EditorSpatialGizmoPlugin(); diff --git a/editor/plugins/texture_region_editor_plugin.cpp b/editor/plugins/texture_region_editor_plugin.cpp index 0419c3d4b1..4a9cbfe535 100644 --- a/editor/plugins/texture_region_editor_plugin.cpp +++ b/editor/plugins/texture_region_editor_plugin.cpp @@ -70,7 +70,7 @@ void TextureRegionEditor::_region_draw() { VS::get_singleton()->canvas_item_add_set_transform(edit_draw->get_canvas_item(), Transform2D()); if (snap_mode == SNAP_GRID) { - Color grid_color = get_color("grid_major_color", "Editor"); + Color grid_color = Color(1.0, 1.0, 1.0, 0.15); Size2 s = edit_draw->get_size(); int last_cell = 0; @@ -603,7 +603,6 @@ void TextureRegionEditor::_notification(int p_what) { zoom_out->set_icon(get_icon("ZoomLess", "EditorIcons")); zoom_reset->set_icon(get_icon("ZoomReset", "EditorIcons")); zoom_in->set_icon(get_icon("ZoomMore", "EditorIcons")); - icon_zoom->set_texture(get_icon("Zoom", "EditorIcons")); } break; } } @@ -865,7 +864,7 @@ TextureRegionEditor::TextureRegionEditor(EditorNode *p_editor) { hb_grid->add_child(sb_step_y); hb_grid->add_child(memnew(VSeparator)); - hb_grid->add_child(memnew(Label(TTR("Separation:")))); + hb_grid->add_child(memnew(Label(TTR("Sep.:")))); sb_sep_x = memnew(SpinBox); sb_sep_x->set_min(0); @@ -898,10 +897,6 @@ TextureRegionEditor::TextureRegionEditor(EditorNode *p_editor) { separator->set_h_size_flags(Control::SIZE_EXPAND_FILL); hb_tools->add_child(separator); - icon_zoom = memnew(TextureRect); - icon_zoom->set_stretch_mode(TextureRect::STRETCH_KEEP_ASPECT_CENTERED); - hb_tools->add_child(icon_zoom); - zoom_out = memnew(ToolButton); zoom_out->connect("pressed", this, "_zoom_out"); hb_tools->add_child(zoom_out); @@ -940,16 +935,15 @@ bool TextureRegionEditorPlugin::handles(Object *p_object) const { void TextureRegionEditorPlugin::make_visible(bool p_visible) { if (p_visible) { texture_region_button->show(); - if (region_editor->is_stylebox() || region_editor->is_atlas_texture() || region_editor->is_ninepatch() || (region_editor->get_sprite() && region_editor->get_sprite()->is_region())) { + if (region_editor->is_stylebox() || region_editor->is_atlas_texture() || region_editor->is_ninepatch() || (region_editor->get_sprite() && region_editor->get_sprite()->is_region()) || texture_region_button->is_pressed()) { editor->make_bottom_panel_item_visible(region_editor); - } else { - if (texture_region_button->is_pressed()) - region_editor->show(); } } else { + if (region_editor->is_visible_in_tree()) { + editor->hide_bottom_panel(); + } texture_region_button->hide(); region_editor->edit(NULL); - region_editor->hide(); } } @@ -1001,10 +995,9 @@ TextureRegionEditorPlugin::TextureRegionEditorPlugin(EditorNode *p_node) { editor = p_node; region_editor = memnew(TextureRegionEditor(p_node)); - texture_region_button = p_node->add_bottom_panel_item(TTR("TextureRegion"), region_editor); - texture_region_button->set_tooltip(TTR("Texture Region Editor")); - region_editor->set_custom_minimum_size(Size2(0, 200) * EDSCALE); region_editor->hide(); + + texture_region_button = p_node->add_bottom_panel_item(TTR("TextureRegion"), region_editor); texture_region_button->hide(); } diff --git a/editor/plugins/texture_region_editor_plugin.h b/editor/plugins/texture_region_editor_plugin.h index bd93be9267..670cc86bbb 100644 --- a/editor/plugins/texture_region_editor_plugin.h +++ b/editor/plugins/texture_region_editor_plugin.h @@ -56,7 +56,6 @@ class TextureRegionEditor : public Control { friend class TextureRegionEditorPlugin; MenuButton *snap_mode_button; - TextureRect *icon_zoom; ToolButton *zoom_in; ToolButton *zoom_reset; ToolButton *zoom_out; diff --git a/editor/plugins/tile_map_editor_plugin.cpp b/editor/plugins/tile_map_editor_plugin.cpp index 0b84535c19..3d14db7d0e 100644 --- a/editor/plugins/tile_map_editor_plugin.cpp +++ b/editor/plugins/tile_map_editor_plugin.cpp @@ -73,7 +73,8 @@ void TileMapEditor::_notification(int p_what) { rotate_180->set_icon(get_icon("Rotate180", "EditorIcons")); rotate_270->set_icon(get_icon("Rotate270", "EditorIcons")); - search_box->add_icon_override("right_icon", get_icon("Search", "EditorIcons")); + search_box->set_right_icon(get_icon("Search", "EditorIcons")); + search_box->set_clear_button_enabled(true); PopupMenu *p = options->get_popup(); p->set_item_icon(p->get_item_index(OPTION_PAINTING), get_icon("Edit", "EditorIcons")); @@ -532,10 +533,9 @@ PoolVector<Vector2> TileMapEditor::_bucket_fill(const Point2i &p_start, bool era return PoolVector<Vector2>(); } - for (int i = ids.size() - 1; i >= 0; i--) { - if (ids[i] == prev_id) { - return PoolVector<Vector2>(); - } + if (ids.size() == 1 && ids[0] == prev_id) { + // Same ID, nothing to change + return PoolVector<Vector2>(); } Rect2i r = node->get_used_rect(); diff --git a/editor/plugins/tile_set_editor_plugin.cpp b/editor/plugins/tile_set_editor_plugin.cpp index 8d1db5de8f..490ebeca26 100644 --- a/editor/plugins/tile_set_editor_plugin.cpp +++ b/editor/plugins/tile_set_editor_plugin.cpp @@ -307,6 +307,11 @@ TileSetEditor::TileSetEditor(EditorNode *p_editor) { tool_workspacemode[i]->connect("pressed", this, "_on_workspace_mode_changed", p); tool_hb->add_child(tool_workspacemode[i]); } + Control *spacer = memnew(Control); + spacer->set_h_size_flags(Control::SIZE_EXPAND_FILL); + tool_hb->add_child(spacer); + tool_hb->move_child(spacer, (int)WORKSPACE_CREATE_SINGLE); + tool_workspacemode[WORKSPACE_EDIT]->set_pressed(true); workspace_mode = WORKSPACE_EDIT; @@ -314,9 +319,6 @@ TileSetEditor::TileSetEditor(EditorNode *p_editor) { main_vb->add_child(memnew(HSeparator)); tool_hb = memnew(HBoxContainer); - Control *spacer = memnew(Control); - spacer->set_custom_minimum_size(Size2(30, 0)); - tool_hb->add_child(spacer); g = Ref<ButtonGroup>(memnew(ButtonGroup)); String label[EDITMODE_MAX] = { "Region", "Collision", "Occlusion", "Navigation", "Bitmask", "Priority", "Icon" }; @@ -335,7 +337,8 @@ TileSetEditor::TileSetEditor(EditorNode *p_editor) { edit_mode = EDITMODE_COLLISION; main_vb->add_child(tool_hb); - main_vb->add_child(memnew(HSeparator)); + separator_editmode = memnew(HSeparator); + main_vb->add_child(separator_editmode); toolbar = memnew(HBoxContainer); Ref<ButtonGroup> tg(memnew(ButtonGroup)); @@ -369,13 +372,17 @@ TileSetEditor::TileSetEditor(EditorNode *p_editor) { toolbar->add_child(tools[SHAPE_NEW_POLYGON]); tools[SHAPE_NEW_POLYGON]->set_toggle_mode(true); tools[SHAPE_NEW_POLYGON]->set_button_group(tg); - toolbar->add_child(memnew(VSeparator)); + + separator_delete = memnew(VSeparator); + toolbar->add_child(separator_delete); tools[SHAPE_DELETE] = memnew(ToolButton); p = Vector<Variant>(); p.push_back((int)SHAPE_DELETE); tools[SHAPE_DELETE]->connect("pressed", this, "_on_tool_clicked", p); toolbar->add_child(tools[SHAPE_DELETE]); - toolbar->add_child(memnew(VSeparator)); + + separator_grid = memnew(VSeparator); + toolbar->add_child(separator_grid); tools[SHAPE_KEEP_INSIDE_TILE] = memnew(ToolButton); tools[SHAPE_KEEP_INSIDE_TILE]->set_toggle_mode(true); tools[SHAPE_KEEP_INSIDE_TILE]->set_pressed(true); @@ -588,10 +595,16 @@ void TileSetEditor::_on_edit_mode_changed(int p_edit_mode) { tools[BITMASK_PASTE]->hide(); tools[BITMASK_CLEAR]->hide(); tools[SHAPE_NEW_POLYGON]->hide(); - if (workspace_mode == WORKSPACE_EDIT) + + if (workspace_mode == WORKSPACE_EDIT) { + separator_delete->show(); tools[SHAPE_DELETE]->show(); - else + } else { + separator_delete->hide(); tools[SHAPE_DELETE]->hide(); + } + + separator_grid->show(); tools[SHAPE_KEEP_INSIDE_TILE]->hide(); tools[TOOL_GRID_SNAP]->show(); @@ -605,7 +618,11 @@ void TileSetEditor::_on_edit_mode_changed(int p_edit_mode) { tools[BITMASK_PASTE]->show(); tools[BITMASK_CLEAR]->show(); tools[SHAPE_NEW_POLYGON]->hide(); + + separator_delete->hide(); tools[SHAPE_DELETE]->hide(); + + separator_grid->hide(); tools[SHAPE_KEEP_INSIDE_TILE]->hide(); tools[TOOL_GRID_SNAP]->hide(); @@ -621,7 +638,11 @@ void TileSetEditor::_on_edit_mode_changed(int p_edit_mode) { tools[BITMASK_PASTE]->hide(); tools[BITMASK_CLEAR]->hide(); tools[SHAPE_NEW_POLYGON]->show(); + + separator_delete->show(); tools[SHAPE_DELETE]->show(); + + separator_grid->show(); tools[SHAPE_KEEP_INSIDE_TILE]->show(); tools[TOOL_GRID_SNAP]->show(); @@ -635,9 +656,14 @@ void TileSetEditor::_on_edit_mode_changed(int p_edit_mode) { tools[BITMASK_PASTE]->hide(); tools[BITMASK_CLEAR]->hide(); tools[SHAPE_NEW_POLYGON]->hide(); + + separator_delete->hide(); tools[SHAPE_DELETE]->hide(); + + separator_grid->show(); tools[SHAPE_KEEP_INSIDE_TILE]->hide(); tools[TOOL_GRID_SNAP]->show(); + if (edit_mode == EDITMODE_ICON) { tools[TOOL_SELECT]->set_tooltip(TTR("Select sub-tile to use as icon, this will be also used on invalid autotile bindings.\nClick on another Tile to edit it.")); spin_priority->hide(); @@ -661,6 +687,7 @@ void TileSetEditor::_on_workspace_mode_changed(int p_workspace_mode) { tool_editmode[EDITMODE_REGION]->show(); tool_editmode[EDITMODE_REGION]->set_pressed(true); _on_edit_mode_changed(EDITMODE_REGION); + separator_editmode->show(); } } @@ -2076,6 +2103,7 @@ void TileSetEditor::update_workspace_tile_mode() { tool_editmode[EDITMODE_REGION]->show(); tool_editmode[EDITMODE_REGION]->set_pressed(true); _on_edit_mode_changed(EDITMODE_REGION); + separator_editmode->show(); return; } @@ -2086,12 +2114,16 @@ void TileSetEditor::update_workspace_tile_mode() { for (int i = 0; i < ZOOM_OUT; i++) { tools[i]->hide(); } + separator_editmode->hide(); + separator_delete->hide(); + separator_grid->hide(); return; } for (int i = 0; i < EDITMODE_MAX; i++) { tool_editmode[i]->show(); } + separator_editmode->show(); if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::SINGLE_TILE) { if (tool_editmode[EDITMODE_ICON]->is_pressed() || tool_editmode[EDITMODE_PRIORITY]->is_pressed() || tool_editmode[EDITMODE_BITMASK]->is_pressed()) { @@ -2336,12 +2368,10 @@ bool TileSetEditorPlugin::handles(Object *p_node) const { void TileSetEditorPlugin::make_visible(bool p_visible) { if (p_visible) { tileset_editor_button->show(); - if (tileset_editor_button->is_pressed()) { - tileset_editor->show(); - } + editor->make_bottom_panel_item_visible(tileset_editor); get_tree()->connect("idle_frame", tileset_editor, "_on_workspace_process"); } else { - tileset_editor->hide(); + editor->hide_bottom_panel(); tileset_editor_button->hide(); get_tree()->disconnect("idle_frame", tileset_editor, "_on_workspace_process"); } diff --git a/editor/plugins/tile_set_editor_plugin.h b/editor/plugins/tile_set_editor_plugin.h index 0c175e718c..23bf68b90f 100644 --- a/editor/plugins/tile_set_editor_plugin.h +++ b/editor/plugins/tile_set_editor_plugin.h @@ -40,12 +40,12 @@ #define WORKSPACE_MARGIN Vector2(10, 10) class TilesetEditorContext; -class TileSetEditor : public Panel { +class TileSetEditor : public Control { friend class TileSetEditorPlugin; friend class TilesetEditorContext; - GDCLASS(TileSetEditor, Panel) + GDCLASS(TileSetEditor, Control) enum TextureToolButtons { TOOL_TILESET_ADD_TEXTURE, @@ -131,8 +131,11 @@ class TileSetEditor : public Panel { Control *workspace; Button *tool_workspacemode[WORKSPACE_MODE_MAX]; Button *tool_editmode[EDITMODE_MAX]; + HSeparator *separator_editmode; HBoxContainer *toolbar; ToolButton *tools[TOOL_MAX]; + VSeparator *separator_delete; + VSeparator *separator_grid; SpinBox *spin_priority; WorkspaceMode workspace_mode; EditMode edit_mode; diff --git a/editor/plugins/visual_shader_editor_plugin.cpp b/editor/plugins/visual_shader_editor_plugin.cpp index 9218fed907..63e89b78ea 100644 --- a/editor/plugins/visual_shader_editor_plugin.cpp +++ b/editor/plugins/visual_shader_editor_plugin.cpp @@ -119,6 +119,9 @@ void VisualShaderEditor::_update_graph() { if (updating) return; + if (visual_shader.is_null()) + return; + graph->set_scroll_ofs(visual_shader->get_graph_offset() * EDSCALE); VisualShader::Type type = VisualShader::Type(edit_type->get_selected()); diff --git a/editor/project_export.cpp b/editor/project_export.cpp index 170546f14c..019d5d382c 100644 --- a/editor/project_export.cpp +++ b/editor/project_export.cpp @@ -29,6 +29,7 @@ /*************************************************************************/ #include "project_export.h" + #include "compressed_translation.h" #include "editor_data.h" #include "editor_node.h" @@ -231,7 +232,7 @@ void ProjectExportDialog::_edit_preset(int p_index) { if (error != String()) { - Vector<String> items = error.split("\n"); + Vector<String> items = error.split("\n", false); error = ""; for (int i = 0; i < items.size(); i++) { if (i > 0) @@ -389,7 +390,6 @@ void ProjectExportDialog::_patch_deleted() { void ProjectExportDialog::_update_parameters(const String &p_edited_property) { _edit_preset(presets->get_current()); - parameters->update_tree(); } void ProjectExportDialog::_runnable_pressed() { @@ -756,7 +756,7 @@ void ProjectExportDialog::_export_project_to_path(const String &p_path) { Error err = platform->export_project(current, export_debug->is_pressed(), p_path, 0); if (err != OK) { - error_dialog->set_text(TTR("Export templates for this platform are missing/corrupted: ") + platform->get_name()); + error_dialog->set_text(TTR("Export templates for this platform are missing/corrupted:") + " " + platform->get_name()); error_dialog->show(); error_dialog->popup_centered_minsize(Size2(300, 80)); ERR_PRINT("Failed to export project"); @@ -842,12 +842,10 @@ ProjectExportDialog::ProjectExportDialog() { settings_vb->add_child(sections); sections->set_v_size_flags(SIZE_EXPAND_FILL); - parameters = memnew(PropertyEditor); + parameters = memnew(EditorInspector); sections->add_child(parameters); parameters->set_name(TTR("Options")); - parameters->hide_top_label(); parameters->set_v_size_flags(SIZE_EXPAND_FILL); - parameters->connect("property_edited", this, "_update_parameters"); VBoxContainer *resources_vb = memnew(VBoxContainer); @@ -956,7 +954,7 @@ ProjectExportDialog::ProjectExportDialog() { export_error = memnew(Label); main_vb->add_child(export_error); export_error->hide(); - export_error->add_color_override("font_color", get_color("error_color", "Editor")); + export_error->add_color_override("font_color", EditorNode::get_singleton()->get_gui_base()->get_color("error_color", "Editor")); export_templates_error = memnew(HBoxContainer); main_vb->add_child(export_templates_error); @@ -964,7 +962,7 @@ ProjectExportDialog::ProjectExportDialog() { Label *export_error2 = memnew(Label); export_templates_error->add_child(export_error2); - export_error2->add_color_override("font_color", get_color("error_color", "Editor")); + export_error2->add_color_override("font_color", EditorNode::get_singleton()->get_gui_base()->get_color("error_color", "Editor")); export_error2->set_text(" - " + TTR("Export templates for this platform are missing:") + " "); error_dialog = memnew(AcceptDialog); @@ -975,6 +973,7 @@ ProjectExportDialog::ProjectExportDialog() { LinkButton *download_templates = memnew(LinkButton); download_templates->set_text(TTR("Manage Export Templates")); + download_templates->set_v_size_flags(SIZE_SHRINK_CENTER); export_templates_error->add_child(download_templates); download_templates->connect("pressed", this, "_open_export_template_manager"); diff --git a/editor/project_export.h b/editor/project_export.h index b62254974d..1f8723febd 100644 --- a/editor/project_export.h +++ b/editor/project_export.h @@ -31,26 +31,27 @@ #ifndef PROJECT_EXPORT_SETTINGS_H #define PROJECT_EXPORT_SETTINGS_H +#include "core/os/dir_access.h" +#include "core/os/thread.h" +#include "editor/editor_export.h" #include "editor/editor_file_dialog.h" -#include "os/dir_access.h" -#include "os/thread.h" +#include "editor/editor_file_system.h" +#include "editor/editor_inspector.h" #include "scene/gui/button.h" +#include "scene/gui/check_button.h" #include "scene/gui/control.h" #include "scene/gui/dialogs.h" #include "scene/gui/file_dialog.h" #include "scene/gui/label.h" #include "scene/gui/link_button.h" +#include "scene/gui/menu_button.h" #include "scene/gui/option_button.h" #include "scene/gui/rich_text_label.h" +#include "scene/gui/slider.h" #include "scene/gui/tab_container.h" #include "scene/gui/tree.h" #include "scene/main/timer.h" -#include "editor/editor_file_system.h" -#include "editor_export.h" -#include "property_editor.h" -#include "scene/gui/slider.h" - class EditorNode; class ProjectExportDialog : public ConfirmationDialog { @@ -64,12 +65,9 @@ private: ItemList *presets; LineEdit *name; - PropertyEditor *parameters; + EditorInspector *parameters; CheckButton *runnable; - //EditorFileDialog *pck_export; - //EditorFileDialog *file_export; - Button *button_export; bool updating; diff --git a/editor/project_manager.cpp b/editor/project_manager.cpp index 95d39953cf..aad9258ed9 100644 --- a/editor/project_manager.cpp +++ b/editor/project_manager.cpp @@ -915,12 +915,6 @@ void ProjectManager::_update_project_buttons() { CanvasItem *item = Object::cast_to<CanvasItem>(scroll_children->get_child(i)); item->update(); - - Button *show = Object::cast_to<Button>(item->get_node(NodePath("project/path_box/show"))); - if (show) { - String current = item->get_meta("name"); - show->set_visible(selected_list.has(current)); - } } bool empty_selection = selected_list.empty(); @@ -1316,7 +1310,6 @@ void ProjectManager::_load_recent_projects() { path_hb->add_child(show); show->connect("pressed", this, "_show_project", varray(path)); show->set_tooltip(TTR("Show In File Manager")); - show->set_visible(false); Label *fpath = memnew(Label(path)); fpath->set_name("path"); @@ -1757,6 +1750,8 @@ ProjectManager::ProjectManager() { editor_set_scale(custom_display_scale); } break; } + + OS::get_singleton()->set_window_size(OS::get_singleton()->get_window_size() * MAX(1, EDSCALE)); } FileDialog::set_default_show_hidden_files(EditorSettings::get_singleton()->get("filesystem/file_dialog/show_hidden_files")); @@ -1782,7 +1777,6 @@ ProjectManager::ProjectManager() { String cp; cp += 0xA9; - cp += '0'; OS::get_singleton()->set_window_title(VERSION_NAME + String(" - ") + TTR("Project Manager") + " - " + cp + " 2007-2018 Juan Linietsky, Ariel Manzur & Godot Contributors"); HBoxContainer *top_hb = memnew(HBoxContainer); @@ -1825,7 +1819,7 @@ ProjectManager::ProjectManager() { project_filter = memnew(ProjectListFilter); search_box->add_child(project_filter); project_filter->connect("filter_changed", this, "_load_recent_projects"); - project_filter->set_custom_minimum_size(Size2(250, 10)); + project_filter->set_custom_minimum_size(Size2(280, 10) * EDSCALE); search_tree_vb->add_child(search_box); PanelContainer *pc = memnew(PanelContainer); @@ -2023,18 +2017,6 @@ void ProjectListFilter::_setup_filters() { filter_option->add_item(TTR("Path")); } -void ProjectListFilter::_command(int p_command) { - switch (p_command) { - - case CMD_CLEAR_FILTER: { - if (search_box->get_text() != "") { - search_box->clear(); - emit_signal("filter_changed"); - } - } break; - } -} - void ProjectListFilter::_search_text_changed(const String &p_newtext) { emit_signal("filter_changed"); } @@ -2057,13 +2039,14 @@ void ProjectListFilter::_filter_option_selected(int p_idx) { void ProjectListFilter::_notification(int p_what) { - if (p_what == NOTIFICATION_ENTER_TREE) - clear_search_button->set_icon(get_icon("Close", "EditorIcons")); + if (p_what == NOTIFICATION_ENTER_TREE) { + search_box->set_right_icon(get_icon("Search", "EditorIcons")); + search_box->set_clear_button_enabled(true); + } } void ProjectListFilter::_bind_methods() { - ClassDB::bind_method(D_METHOD("_command"), &ProjectListFilter::_command); ClassDB::bind_method(D_METHOD("_search_text_changed"), &ProjectListFilter::_search_text_changed); ClassDB::bind_method(D_METHOD("_filter_option_selected"), &ProjectListFilter::_filter_option_selected); @@ -2088,8 +2071,4 @@ ProjectListFilter::ProjectListFilter() { search_box->connect("text_changed", this, "_search_text_changed"); search_box->set_h_size_flags(SIZE_EXPAND_FILL); add_child(search_box); - - clear_search_button = memnew(ToolButton); - clear_search_button->connect("pressed", this, "_command", make_binds(CMD_CLEAR_FILTER)); - add_child(clear_search_button); } diff --git a/editor/project_manager.h b/editor/project_manager.h index a9d23b1f71..ad21e00b0d 100644 --- a/editor/project_manager.h +++ b/editor/project_manager.h @@ -128,13 +128,8 @@ class ProjectListFilter : public HBoxContainer { private: friend class ProjectManager; - enum Command { - CMD_CLEAR_FILTER, - }; - OptionButton *filter_option; LineEdit *search_box; - ToolButton *clear_search_button; enum FilterOption { FILTER_NAME, @@ -142,7 +137,6 @@ private: }; FilterOption _current_filter; - void _command(int p_command); void _search_text_changed(const String &p_newtext); void _setup_filters(); void _filter_option_selected(int p_idx); diff --git a/editor/project_settings_editor.cpp b/editor/project_settings_editor.cpp index 2b2e03ce38..970302e058 100644 --- a/editor/project_settings_editor.cpp +++ b/editor/project_settings_editor.cpp @@ -81,7 +81,8 @@ void ProjectSettingsEditor::_notification(int p_what) { globals_editor->edit(ProjectSettings::get_singleton()); search_button->set_icon(get_icon("Search", "EditorIcons")); - clear_button->set_icon(get_icon("Close", "EditorIcons")); + search_box->set_right_icon(get_icon("Search", "EditorIcons")); + search_box->set_clear_button_enabled(true); action_add_error->add_color_override("font_color", get_color("error_color", "Editor")); @@ -119,7 +120,8 @@ void ProjectSettingsEditor::_notification(int p_what) { case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: { search_button->set_icon(get_icon("Search", "EditorIcons")); - clear_button->set_icon(get_icon("Close", "EditorIcons")); + search_box->set_right_icon(get_icon("Search", "EditorIcons")); + search_box->set_clear_button_enabled(true); action_add_error->add_color_override("font_color", get_color("error_color", "Editor")); popup_add->set_item_icon(popup_add->get_item_index(INPUT_KEY), get_icon("Keyboard", "EditorIcons")); popup_add->set_item_icon(popup_add->get_item_index(INPUT_JOY_BUTTON), get_icon("JoyButton", "EditorIcons")); @@ -213,10 +215,8 @@ void ProjectSettingsEditor::_action_edited() { undo_redo->create_action(TTR("Change Action deadzone")); undo_redo->add_do_method(ProjectSettings::get_singleton(), "set", name, new_action); - undo_redo->add_do_method(this, "_update_actions"); undo_redo->add_do_method(this, "_settings_changed"); undo_redo->add_undo_method(ProjectSettings::get_singleton(), "set", name, old_action); - undo_redo->add_undo_method(this, "_update_actions"); undo_redo->add_undo_method(this, "_settings_changed"); undo_redo->commit_action(); } @@ -806,6 +806,10 @@ void ProjectSettingsEditor::popup_project_settings() { plugin_settings->update_plugins(); } +void ProjectSettingsEditor::update_plugins() { + plugin_settings->update_plugins(); +} + void ProjectSettingsEditor::_item_selected(const String &p_path) { String selected_path = p_path; @@ -1591,15 +1595,6 @@ void ProjectSettingsEditor::_toggle_search_bar(bool p_pressed) { } } -void ProjectSettingsEditor::_clear_search_box() { - - if (search_box->get_text() == "") - return; - - search_box->clear(); - globals_editor->get_inspector()->update_tree(); -} - void ProjectSettingsEditor::set_plugins_page() { tab_container->set_current_tab(plugin_settings->get_index()); @@ -1662,7 +1657,6 @@ void ProjectSettingsEditor::_bind_methods() { ClassDB::bind_method(D_METHOD("_translation_filter_option_changed"), &ProjectSettingsEditor::_translation_filter_option_changed); ClassDB::bind_method(D_METHOD("_translation_filter_mode_changed"), &ProjectSettingsEditor::_translation_filter_mode_changed); - ClassDB::bind_method(D_METHOD("_clear_search_box"), &ProjectSettingsEditor::_clear_search_box); ClassDB::bind_method(D_METHOD("_toggle_search_bar"), &ProjectSettingsEditor::_toggle_search_bar); ClassDB::bind_method(D_METHOD("_copy_to_platform_about_to_show"), &ProjectSettingsEditor::_copy_to_platform_about_to_show); @@ -1753,10 +1747,6 @@ ProjectSettingsEditor::ProjectSettingsEditor(EditorData *p_data) { search_box->set_h_size_flags(Control::SIZE_EXPAND_FILL); search_bar->add_child(search_box); - clear_button = memnew(ToolButton); - search_bar->add_child(clear_button); - clear_button->connect("pressed", this, "_clear_search_box"); - globals_editor = memnew(SectionedInspector); props_base->add_child(globals_editor); globals_editor->get_inspector()->set_undo_redo(EditorNode::get_singleton()->get_undo_redo()); diff --git a/editor/project_settings_editor.h b/editor/project_settings_editor.h index 3b74ae1909..1344da1de7 100644 --- a/editor/project_settings_editor.h +++ b/editor/project_settings_editor.h @@ -69,7 +69,6 @@ class ProjectSettingsEditor : public AcceptDialog { HBoxContainer *search_bar; Button *search_button; LineEdit *search_box; - ToolButton *clear_button; HBoxContainer *add_prop_bar; AcceptDialog *message; @@ -158,7 +157,6 @@ class ProjectSettingsEditor : public AcceptDialog { void _translation_filter_mode_changed(int p_mode); void _toggle_search_bar(bool p_pressed); - void _clear_search_box(); void _copy_to_platform_about_to_show(); @@ -188,6 +186,7 @@ public: static ProjectSettingsEditor *get_singleton() { return singleton; } void popup_project_settings(); void set_plugins_page(); + void update_plugins(); EditorAutoloadSettings *get_autoload_settings() { return autoload_settings; } diff --git a/editor/property_editor.cpp b/editor/property_editor.cpp index b370a711e3..408e67149a 100644 --- a/editor/property_editor.cpp +++ b/editor/property_editor.cpp @@ -287,7 +287,11 @@ void CustomPropertyEditor::_menu_option(int p_which) { Object *obj = ClassDB::instance(intype); if (!obj) { - obj = EditorNode::get_editor_data().instance_custom_type(intype, "Resource"); + if (ScriptServer::is_global_class(intype)) { + obj = EditorNode::get_editor_data().script_class_instance(intype); + } else { + obj = EditorNode::get_editor_data().instance_custom_type(intype, "Resource"); + } } ERR_BREAK(!obj); @@ -1132,7 +1136,11 @@ void CustomPropertyEditor::_type_create_selected(int p_idx) { Object *obj = ClassDB::instance(intype); if (!obj) { - obj = EditorNode::get_editor_data().instance_custom_type(intype, "Resource"); + if (ScriptServer::is_global_class(intype)) { + obj = EditorNode::get_editor_data().script_class_instance(intype); + } else { + obj = EditorNode::get_editor_data().instance_custom_type(intype, "Resource"); + } } ERR_FAIL_COND(!obj); @@ -1334,7 +1342,11 @@ void CustomPropertyEditor::_action_pressed(int p_which) { Object *obj = ClassDB::instance(intype); if (!obj) { - obj = EditorNode::get_editor_data().instance_custom_type(intype, "Resource"); + if (ScriptServer::is_global_class(intype)) { + obj = EditorNode::get_editor_data().script_class_instance(intype); + } else { + obj = EditorNode::get_editor_data().instance_custom_type(intype, "Resource"); + } } ERR_BREAK(!obj); diff --git a/editor/quick_open.cpp b/editor/quick_open.cpp index 907bb50f7e..d2101f1e00 100644 --- a/editor/quick_open.cpp +++ b/editor/quick_open.cpp @@ -258,6 +258,9 @@ void EditorQuickOpen::_notification(int p_what) { if (p_what == NOTIFICATION_ENTER_TREE) { connect("confirmed", this, "_confirmed"); + + search_box->set_right_icon(get_icon("Search", "EditorIcons")); + search_box->set_clear_button_enabled(true); } } diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp index d9419af549..607d974025 100644 --- a/editor/scene_tree_dock.cpp +++ b/editor/scene_tree_dock.cpp @@ -119,7 +119,7 @@ void SceneTreeDock::instance(const String &p_file) { if (!edited_scene) { current_option = -1; - accept->get_ok()->set_text(TTR("OK :(")); + accept->get_ok()->set_text(TTR("OK")); accept->set_text(TTR("No parent to instance a child at.")); accept->popup_centered_minsize(); return; @@ -365,10 +365,14 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { for (int i = 0; i < ScriptServer::get_language_count(); i++) { ScriptLanguage *l = ScriptServer::get_language(i); if (l->get_type() == existing->get_class()) { - if (EDITOR_GET("interface/editors/derive_script_globals_by_name").operator bool()) { - String name = l->get_global_class_name(existing->get_path(), NULL); - inherits = editor->get_editor_data().script_class_get_base(name); - } else if (l->can_inherit_from_file()) { + String name = l->get_global_class_name(existing->get_path()); + if (ScriptServer::is_global_class(name)) { + if (EDITOR_GET("interface/editors/derive_script_globals_by_name").operator bool()) { + inherits = editor->get_editor_data().script_class_get_base(name); + } else if (l->can_inherit_from_file()) { + inherits = "\"" + existing->get_path() + "\""; + } + } else { inherits = "\"" + existing->get_path() + "\""; } } @@ -395,6 +399,9 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { const RefPtr empty; editor_data->get_undo_redo().add_do_method(E->get(), "set_script", empty); editor_data->get_undo_redo().add_undo_method(E->get(), "set_script", existing); + + editor_data->get_undo_redo().add_do_method(E->get(), "set_meta", "_editor_icon", get_icon(E->get()->get_class(), "EditorIcons")); + editor_data->get_undo_redo().add_undo_method(E->get(), "set_meta", "_editor_icon", E->get()->get_meta("_editor_icon")); } } @@ -402,7 +409,6 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { editor_data->get_undo_redo().add_undo_method(this, "_update_script_button"); editor_data->get_undo_redo().commit_action(); - } break; case TOOL_MOVE_UP: case TOOL_MOVE_DOWN: { @@ -797,18 +803,38 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { } break; case TOOL_CREATE_2D_SCENE: case TOOL_CREATE_3D_SCENE: - case TOOL_CREATE_USER_INTERFACE: { + case TOOL_CREATE_USER_INTERFACE: + case TOOL_CREATE_FAVORITE: { Node *new_node; - switch (p_tool) { - case TOOL_CREATE_2D_SCENE: new_node = memnew(Node2D); break; - case TOOL_CREATE_3D_SCENE: new_node = memnew(Spatial); break; - case TOOL_CREATE_USER_INTERFACE: { - Control *node = memnew(Control); - node->set_anchors_and_margins_preset(PRESET_WIDE); //more useful for resizable UIs. - new_node = node; - } break; + if (TOOL_CREATE_FAVORITE == p_tool) { + String name = selected_favorite_root.get_slicec(' ', 0); + if (ScriptServer::is_global_class(name)) { + new_node = Object::cast_to<Node>(ClassDB::instance(ScriptServer::get_global_class_base(name))); + Ref<Script> script = ResourceLoader::load(ScriptServer::get_global_class_path(name), "Script"); + if (new_node && script.is_valid()) { + new_node->set_script(script.get_ref_ptr()); + new_node->set_name(name); + } + } else { + new_node = Object::cast_to<Node>(ClassDB::instance(selected_favorite_root)); + } + if (!new_node) { + ERR_EXPLAIN("Creating root from favorite '" + selected_favorite_root + "' failed. Creating 'Node' instead."); + new_node = memnew(Node); + } + } else { + switch (p_tool) { + case TOOL_CREATE_2D_SCENE: new_node = memnew(Node2D); break; + case TOOL_CREATE_3D_SCENE: new_node = memnew(Spatial); break; + case TOOL_CREATE_USER_INTERFACE: { + Control *node = memnew(Control); + node->set_anchors_and_margins_preset(PRESET_WIDE); //more useful for resizable UIs. + new_node = node; + + } break; + } } editor_data->get_undo_redo().create_action("New Scene Root"); @@ -862,39 +888,67 @@ void SceneTreeDock::_notification(int p_what) { button_create_script->set_icon(get_icon("ScriptCreate", "EditorIcons")); button_clear_script->set_icon(get_icon("ScriptRemove", "EditorIcons")); - filter->add_icon_override("right_icon", get_icon("Search", "EditorIcons")); + filter->set_right_icon(get_icon("Search", "EditorIcons")); + filter->set_clear_button_enabled(true); EditorNode::get_singleton()->get_editor_selection()->connect("selection_changed", this, "_selection_changed"); - create_root_dialog->add_child(memnew(Label(TTR("Create Root Node:")))); + // create_root_dialog + HBoxContainer *top_row = memnew(HBoxContainer); + top_row->set_name("NodeShortcutsTopRow"); + top_row->set_h_size_flags(SIZE_EXPAND_FILL); + top_row->add_child(memnew(Label(TTR("Create Root Node:")))); + top_row->add_spacer(); - Button *button_2d = memnew(Button); - create_root_dialog->add_child(button_2d); + ToolButton *node_shortcuts_toggle = memnew(ToolButton); + node_shortcuts_toggle->set_name("NodeShortcutsToggle"); + node_shortcuts_toggle->set_icon(get_icon("Favorites", "EditorIcons")); + node_shortcuts_toggle->set_toggle_mode(true); + node_shortcuts_toggle->set_pressed(EDITOR_GET("_use_favorites_root_selection")); + node_shortcuts_toggle->set_anchors_and_margins_preset(Control::PRESET_CENTER_RIGHT); + node_shortcuts_toggle->connect("pressed", this, "_update_create_root_dialog"); + top_row->add_child(node_shortcuts_toggle); + + create_root_dialog->add_child(top_row); + VBoxContainer *node_shortcuts = memnew(VBoxContainer); + node_shortcuts->set_name("NodeShortcuts"); + + VBoxContainer *beginner_node_shortcuts = memnew(VBoxContainer); + beginner_node_shortcuts->set_name("BeginnerNodeShortcuts"); + node_shortcuts->add_child(beginner_node_shortcuts); + + Button *button_2d = memnew(Button); + beginner_node_shortcuts->add_child(button_2d); button_2d->set_text(TTR("2D Scene")); button_2d->set_icon(get_icon("Node2D", "EditorIcons")); button_2d->connect("pressed", this, "_tool_selected", make_binds(TOOL_CREATE_2D_SCENE, false)); Button *button_3d = memnew(Button); - create_root_dialog->add_child(button_3d); + beginner_node_shortcuts->add_child(button_3d); button_3d->set_text(TTR("3D Scene")); button_3d->set_icon(get_icon("Spatial", "EditorIcons")); button_3d->connect("pressed", this, "_tool_selected", make_binds(TOOL_CREATE_3D_SCENE, false)); Button *button_ui = memnew(Button); - create_root_dialog->add_child(button_ui); + beginner_node_shortcuts->add_child(button_ui); button_ui->set_text(TTR("User Interface")); button_ui->set_icon(get_icon("Control", "EditorIcons")); button_ui->connect("pressed", this, "_tool_selected", make_binds(TOOL_CREATE_USER_INTERFACE, false)); + VBoxContainer *favorite_node_shortcuts = memnew(VBoxContainer); + favorite_node_shortcuts->set_name("FavoriteNodeShortcuts"); + node_shortcuts->add_child(favorite_node_shortcuts); + Button *button_custom = memnew(Button); - create_root_dialog->add_child(button_custom); + node_shortcuts->add_child(button_custom); button_custom->set_text(TTR("Custom Node")); button_custom->set_icon(get_icon("Add", "EditorIcons")); button_custom->connect("pressed", this, "_tool_selected", make_binds(TOOL_NEW, false)); - create_root_dialog->add_spacer(); - + node_shortcuts->add_spacer(); + create_root_dialog->add_child(node_shortcuts); + _update_create_root_dialog(); } break; case NOTIFICATION_ENTER_TREE: { @@ -910,7 +964,8 @@ void SceneTreeDock::_notification(int p_what) { button_create_script->set_icon(get_icon("ScriptCreate", "EditorIcons")); button_clear_script->set_icon(get_icon("ScriptRemove", "EditorIcons")); - filter->add_icon_override("right_icon", get_icon("Search", "EditorIcons")); + filter->set_right_icon(get_icon("Search", "EditorIcons")); + filter->set_clear_button_enabled(true); } break; case NOTIFICATION_PROCESS: { @@ -1249,7 +1304,7 @@ bool SceneTreeDock::_validate_no_foreign() { if (E->get() != edited_scene && E->get()->get_owner() != edited_scene) { - accept->get_ok()->set_text(TTR("Makes Sense!")); + accept->get_ok()->set_text(TTR("OK")); accept->set_text(TTR("Can't operate on nodes from a foreign scene!")); accept->popup_centered_minsize(); return false; @@ -1257,7 +1312,7 @@ bool SceneTreeDock::_validate_no_foreign() { if (edited_scene->get_scene_inherited_state().is_valid() && edited_scene->get_scene_inherited_state()->find_node_by_path(edited_scene->get_path_to(E->get())) >= 0) { - accept->get_ok()->set_text(TTR("Makes Sense!")); + accept->get_ok()->set_text(TTR("OK")); accept->set_text(TTR("Can't operate on nodes the current scene inherits from!")); accept->popup_centered_minsize(); return false; @@ -1441,9 +1496,23 @@ void SceneTreeDock::_script_created(Ref<Script> p_script) { Ref<Script> existing = E->get()->get_script(); editor_data->get_undo_redo().add_do_method(E->get(), "set_script", p_script.get_ref_ptr()); editor_data->get_undo_redo().add_undo_method(E->get(), "set_script", existing); + + String icon_path; + String name = p_script->get_language()->get_global_class_name(p_script->get_path(), NULL, &icon_path); + if (ScriptServer::is_global_class(name)) { + RES icon = ResourceLoader::load(icon_path); + editor_data->get_undo_redo().add_do_method(E->get(), "set_meta", "_editor_icon", icon); + String existing_name = existing.is_valid() ? existing->get_language()->get_global_class_name(existing->get_path()) : String(); + if (existing.is_null() || !ScriptServer::is_global_class(existing_name)) { + editor_data->get_undo_redo().add_undo_method(E->get(), "set_meta", "_editor_icon", get_icon(E->get()->get_class(), "EditorIcons")); + } else { + editor_data->get_undo_redo().add_undo_method(E->get(), "set_meta", "_editor_icon", editor_data->script_class_get_icon_path(existing_name)); + } + } } editor_data->get_undo_redo().commit_action(); + print_line("test: " + String(Variant(selected.front()->get()->get_meta("_editor_icon")))); editor->push_item(p_script.operator->()); } @@ -2148,6 +2217,67 @@ void SceneTreeDock::_local_tree_selected() { edit_local->set_pressed(true); } +void SceneTreeDock::_update_create_root_dialog() { + + BaseButton *toggle = Object::cast_to<BaseButton>(create_root_dialog->get_node(String("NodeShortcutsTopRow/NodeShortcutsToggle"))); + Node *node_shortcuts = create_root_dialog->get_node(String("NodeShortcuts")); + + if (!toggle || !node_shortcuts) + return; + + Control *beginner_nodes = Object::cast_to<Control>(node_shortcuts->get_node(String("BeginnerNodeShortcuts"))); + Control *favorite_nodes = Object::cast_to<Control>(node_shortcuts->get_node(String("FavoriteNodeShortcuts"))); + + if (!beginner_nodes || !favorite_nodes) + return; + + EditorSettings::get_singleton()->set_setting("_use_favorites_root_selection", toggle->is_pressed()); + EditorSettings::get_singleton()->save(); + if (toggle->is_pressed()) { + + for (int i = 0; i < favorite_nodes->get_child_count(); i++) { + favorite_nodes->get_child(i)->queue_delete(); + } + + FileAccess *f = FileAccess::open(EditorSettings::get_singleton()->get_project_settings_dir().plus_file("favorites.Node"), FileAccess::READ); + + if (f) { + + while (!f->eof_reached()) { + String l = f->get_line().strip_edges(); + + if (l != String()) { + Button *button = memnew(Button); + favorite_nodes->add_child(button); + button->set_text(TTR(l)); + String name = l.get_slicec(' ', 0); + if (ScriptServer::is_global_class(name)) + name = ScriptServer::get_global_class_base(name); + button->set_icon(get_icon(name, "EditorIcons")); + button->connect("pressed", this, "_favorite_root_selected", make_binds(l)); + } + } + + memdelete(f); + } + + if (!favorite_nodes->is_visible_in_tree()) { + favorite_nodes->show(); + beginner_nodes->hide(); + } + } else { + if (!beginner_nodes->is_visible_in_tree()) { + beginner_nodes->show(); + favorite_nodes->hide(); + } + } +} + +void SceneTreeDock::_favorite_root_selected(const String &p_class) { + selected_favorite_root = p_class; + _tool_selected(TOOL_CREATE_FAVORITE, false); +} + void SceneTreeDock::_bind_methods() { ClassDB::bind_method(D_METHOD("_tool_selected"), &SceneTreeDock::_tool_selected, DEFVAL(false)); @@ -2176,6 +2306,8 @@ void SceneTreeDock::_bind_methods() { ClassDB::bind_method(D_METHOD("_remote_tree_selected"), &SceneTreeDock::_remote_tree_selected); ClassDB::bind_method(D_METHOD("_local_tree_selected"), &SceneTreeDock::_local_tree_selected); ClassDB::bind_method(D_METHOD("_update_script_button"), &SceneTreeDock::_update_script_button); + ClassDB::bind_method(D_METHOD("_favorite_root_selected"), &SceneTreeDock::_favorite_root_selected); + ClassDB::bind_method(D_METHOD("_update_create_root_dialog"), &SceneTreeDock::_update_create_root_dialog); ClassDB::bind_method(D_METHOD("instance"), &SceneTreeDock::instance); @@ -2301,6 +2433,8 @@ SceneTreeDock::SceneTreeDock(EditorNode *p_editor, Node *p_scene_root, EditorSel create_dialog->set_base_type("Node"); add_child(create_dialog); create_dialog->connect("create", this, "_create"); + create_dialog->connect("favorites_updated", this, "_update_create_root_dialog"); + EditorFileSystem::get_singleton()->connect("script_classes_updated", create_dialog, "_save_and_update_favorite_list"); rename_dialog = memnew(RenameDialog(scene_tree, &editor_data->get_undo_redo())); add_child(rename_dialog); @@ -2347,11 +2481,12 @@ SceneTreeDock::SceneTreeDock(EditorNode *p_editor, Node *p_scene_root, EditorSel clear_inherit_confirm = memnew(ConfirmationDialog); clear_inherit_confirm->set_text(TTR("Clear Inheritance? (No Undo!)")); - clear_inherit_confirm->get_ok()->set_text(TTR("Clear!")); + clear_inherit_confirm->get_ok()->set_text(TTR("Clear")); add_child(clear_inherit_confirm); set_process_input(true); set_process(true); EDITOR_DEF("interface/editors/show_scene_tree_root_selection", true); + EDITOR_DEF("_use_favorites_root_selection", false); } diff --git a/editor/scene_tree_dock.h b/editor/scene_tree_dock.h index 57f4759747..34a7c98d11 100644 --- a/editor/scene_tree_dock.h +++ b/editor/scene_tree_dock.h @@ -86,6 +86,7 @@ class SceneTreeDock : public VBoxContainer { TOOL_CREATE_2D_SCENE, TOOL_CREATE_3D_SCENE, TOOL_CREATE_USER_INTERFACE, + TOOL_CREATE_FAVORITE, }; @@ -141,6 +142,7 @@ class SceneTreeDock : public VBoxContainer { EditorNode *editor; VBoxContainer *create_root_dialog; + String selected_favorite_root; void _add_children_to_popup(Object *p_obj, int p_depth); @@ -201,6 +203,9 @@ class SceneTreeDock : public VBoxContainer { void _remote_tree_selected(); void _local_tree_selected(); + void _update_create_root_dialog(); + void _favorite_root_selected(const String &p_class); + protected: void _notification(int p_what); static void _bind_methods(); diff --git a/editor/script_editor_debugger.cpp b/editor/script_editor_debugger.cpp index e483fde4bc..746e1cd28f 100644 --- a/editor/script_editor_debugger.cpp +++ b/editor/script_editor_debugger.cpp @@ -734,7 +734,10 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da error_list->set_item_metadata(error_list->get_item_count() - 1, stack); - error_count++; + if (warning) + warning_count++; + else + error_count++; } else if (p_msg == "profile_sig") { //cache a signature @@ -1011,20 +1014,26 @@ void ScriptEditorDebugger::_notification(int p_what) { } } - if (error_count != last_error_count) { + if (error_count != last_error_count || warning_count != last_warning_count) { - if (error_count == 0) { + if (error_count == 0 && warning_count == 0) { error_split->set_name(TTR("Errors")); debugger_button->set_text(TTR("Debugger")); debugger_button->set_icon(Ref<Texture>()); tabs->set_tab_icon(error_split->get_index(), Ref<Texture>()); } else { - error_split->set_name(TTR("Errors") + " (" + itos(error_count) + ")"); - debugger_button->set_text(TTR("Debugger") + " (" + itos(error_count) + ")"); - debugger_button->set_icon(get_icon("Error", "EditorIcons")); - tabs->set_tab_icon(error_split->get_index(), get_icon("Error", "EditorIcons")); + error_split->set_name(TTR("Errors") + " (" + itos(error_count + warning_count) + ")"); + debugger_button->set_text(TTR("Debugger") + " (" + itos(error_count + warning_count) + ")"); + if (error_count == 0) { + debugger_button->set_icon(get_icon("Warning", "EditorIcons")); + tabs->set_tab_icon(error_split->get_index(), get_icon("Warning", "EditorIcons")); + } else { + debugger_button->set_icon(get_icon("Error", "EditorIcons")); + tabs->set_tab_icon(error_split->get_index(), get_icon("Error", "EditorIcons")); + } } last_error_count = error_count; + last_warning_count = warning_count; } if (connection.is_null()) { @@ -1054,6 +1063,7 @@ void ScriptEditorDebugger::_notification(int p_what) { error_list->clear(); error_stack->clear(); error_count = 0; + warning_count = 0; profiler_signature.clear(); //live_edit_root->set_text("/root"); @@ -1198,7 +1208,7 @@ void ScriptEditorDebugger::start() { int remote_port = (int)EditorSettings::get_singleton()->get("network/debug/remote_port"); if (server->listen(remote_port) != OK) { - EditorNode::get_log()->add_message(String("Error listening on port ") + itos(remote_port), true); + EditorNode::get_log()->add_message(String("Error listening on port ") + itos(remote_port), EditorLog::MSG_TYPE_ERROR); return; } @@ -1230,6 +1240,9 @@ void ScriptEditorDebugger::stop() { if (connection.is_valid()) { EditorNode::get_log()->add_message("** Debug Process Stopped **"); connection.unref(); + + reason->set_text(""); + reason->set_tooltip(""); } pending_in_queue = 0; @@ -1750,6 +1763,7 @@ void ScriptEditorDebugger::_clear_errors_list() { error_list->clear(); error_count = 0; + warning_count = 0; _notification(NOTIFICATION_PROCESS); } @@ -2162,9 +2176,11 @@ ScriptEditorDebugger::ScriptEditorDebugger(EditorNode *p_editor) { live_debug = false; last_path_id = false; error_count = 0; + warning_count = 0; hide_on_stop = true; enable_external_editor = false; last_error_count = 0; + last_warning_count = 0; EditorNode::get_singleton()->get_pause_button()->connect("pressed", this, "_paused"); } diff --git a/editor/script_editor_debugger.h b/editor/script_editor_debugger.h index f7fe348b65..ce705aa35b 100644 --- a/editor/script_editor_debugger.h +++ b/editor/script_editor_debugger.h @@ -96,7 +96,9 @@ class ScriptEditorDebugger : public Control { EditorFileDialog *file_dialog; int error_count; + int warning_count; int last_error_count; + int last_warning_count; bool hide_on_stop; bool enable_external_editor; diff --git a/editor/settings_config_dialog.cpp b/editor/settings_config_dialog.cpp index fe379703e5..4ebba73cb3 100644 --- a/editor/settings_config_dialog.cpp +++ b/editor/settings_config_dialog.cpp @@ -114,22 +114,6 @@ void EditorSettingsDialog::popup_edit_settings() { _focus_current_search_box(); } -void EditorSettingsDialog::_clear_search_box() { - - if (search_box->get_text() == "") - return; - - search_box->clear(); - inspector->get_inspector()->update_tree(); -} - -void EditorSettingsDialog::_clear_shortcut_search_box() { - if (shortcut_search_box->get_text() == "") - return; - - shortcut_search_box->clear(); -} - void EditorSettingsDialog::_filter_shortcuts(const String &p_filter) { shortcut_filter = p_filter; _update_shortcuts(); @@ -198,10 +182,10 @@ void EditorSettingsDialog::_unhandled_input(const Ref<InputEvent> &p_event) { void EditorSettingsDialog::_update_icons() { - search_box->add_icon_override("right_icon", get_icon("Search", "EditorIcons")); - shortcut_search_box->add_icon_override("right_icon", get_icon("Search", "EditorIcons")); - clear_button->set_icon(get_icon("Close", "EditorIcons")); - shortcut_clear_button->set_icon(get_icon("Close", "EditorIcons")); + search_box->set_right_icon(get_icon("Search", "EditorIcons")); + search_box->set_clear_button_enabled(true); + shortcut_search_box->set_right_icon(get_icon("Search", "EditorIcons")); + shortcut_search_box->set_clear_button_enabled(true); restart_close_button->set_icon(get_icon("Close", "EditorIcons")); restart_container->add_style_override("panel", get_stylebox("bg", "Tree")); @@ -411,8 +395,6 @@ void EditorSettingsDialog::_bind_methods() { ClassDB::bind_method(D_METHOD("_settings_save"), &EditorSettingsDialog::_settings_save); ClassDB::bind_method(D_METHOD("_settings_changed"), &EditorSettingsDialog::_settings_changed); ClassDB::bind_method(D_METHOD("_settings_property_edited"), &EditorSettingsDialog::_settings_property_edited); - ClassDB::bind_method(D_METHOD("_clear_search_box"), &EditorSettingsDialog::_clear_search_box); - ClassDB::bind_method(D_METHOD("_clear_shortcut_search_box"), &EditorSettingsDialog::_clear_shortcut_search_box); ClassDB::bind_method(D_METHOD("_shortcut_button_pressed"), &EditorSettingsDialog::_shortcut_button_pressed); ClassDB::bind_method(D_METHOD("_filter_shortcuts"), &EditorSettingsDialog::_filter_shortcuts); ClassDB::bind_method(D_METHOD("_update_shortcuts"), &EditorSettingsDialog::_update_shortcuts); @@ -451,10 +433,6 @@ EditorSettingsDialog::EditorSettingsDialog() { search_box->set_h_size_flags(Control::SIZE_EXPAND_FILL); hbc->add_child(search_box); - clear_button = memnew(ToolButton); - hbc->add_child(clear_button); - clear_button->connect("pressed", this, "_clear_search_box"); - inspector = memnew(SectionedInspector); //inspector->hide_top_label(); inspector->get_inspector()->set_use_filter(true); @@ -500,10 +478,6 @@ EditorSettingsDialog::EditorSettingsDialog() { hbc->add_child(shortcut_search_box); shortcut_search_box->connect("text_changed", this, "_filter_shortcuts"); - shortcut_clear_button = memnew(ToolButton); - hbc->add_child(shortcut_clear_button); - shortcut_clear_button->connect("pressed", this, "_clear_shortcut_search_box"); - shortcuts = memnew(Tree); tab_shortcuts->add_child(shortcuts, true); shortcuts->set_v_size_flags(SIZE_EXPAND_FILL); diff --git a/editor/settings_config_dialog.h b/editor/settings_config_dialog.h index 6cf2eb6bdf..37d32e401d 100644 --- a/editor/settings_config_dialog.h +++ b/editor/settings_config_dialog.h @@ -52,8 +52,6 @@ class EditorSettingsDialog : public AcceptDialog { LineEdit *search_box; LineEdit *shortcut_search_box; - ToolButton *clear_button; - ToolButton *shortcut_clear_button; SectionedInspector *inspector; Timer *timer; diff --git a/editor/spatial_editor_gizmos.cpp b/editor/spatial_editor_gizmos.cpp index b0505eebda..889d34544a 100644 --- a/editor/spatial_editor_gizmos.cpp +++ b/editor/spatial_editor_gizmos.cpp @@ -48,7 +48,7 @@ // It's so ugly it will eat them alive // The previous comment is kept only for historical reasons. -// No children will be harmed by the visioning of this file... hopefully. +// No children will be harmed by the viewing of this file... hopefully. #define HANDLE_HALF_SIZE 9.5 @@ -531,6 +531,8 @@ bool EditorSpatialGizmo::intersect_ray(Camera *p_camera, const Point2 &p_point, rect.set_position(center - rect.get_size() / 2.0); if (rect.has_point(p_point)) { + r_pos = t.origin; + r_normal = -p_camera->project_ray_normal(p_point); return true; } @@ -675,7 +677,7 @@ void EditorSpatialGizmo::_bind_methods() { ClassDB::bind_method(D_METHOD("add_collision_segments", "segments"), &EditorSpatialGizmo::add_collision_segments); ClassDB::bind_method(D_METHOD("add_collision_triangles", "triangles"), &EditorSpatialGizmo::add_collision_triangles); ClassDB::bind_method(D_METHOD("add_unscaled_billboard", "material", "default_scale"), &EditorSpatialGizmo::add_unscaled_billboard, DEFVAL(1)); - ClassDB::bind_method(D_METHOD("add_handles", "handles", "billboard", "secondary"), &EditorSpatialGizmo::add_handles, DEFVAL(false), DEFVAL(false)); + ClassDB::bind_method(D_METHOD("add_handles", "handles", "material", "billboard", "secondary"), &EditorSpatialGizmo::add_handles, DEFVAL(false), DEFVAL(false)); ClassDB::bind_method(D_METHOD("set_spatial_node", "node"), &EditorSpatialGizmo::_set_spatial_node); ClassDB::bind_method(D_METHOD("clear"), &EditorSpatialGizmo::clear); ClassDB::bind_method(D_METHOD("set_hidden", "hidden"), &EditorSpatialGizmo::set_hidden); @@ -1472,34 +1474,6 @@ void SkeletonSpatialGizmoPlugin::redraw(EditorSpatialGizmo *p_gizmo) { p_gizmo->clear(); Ref<Material> material = get_material("skeleton_material", p_gizmo); - SpatialMaterial *sm = Object::cast_to<SpatialMaterial>(material.ptr()); - - { // Reset - Color c(sm->get_albedo()); - c.a = 1; - sm->set_albedo(c); - } - if (sm) { - switch (SpatialEditor::get_singleton()->get_skeleton_visibility_state()) { - case 0: { - // Hidden - Color c(sm->get_albedo()); - c.a = 0; - sm->set_albedo(c); - sm->set_feature(SpatialMaterial::FEATURE_TRANSPARENT, true); - } break; - case 1: - // Visible - sm->set_feature(SpatialMaterial::FEATURE_TRANSPARENT, false); - sm->set_render_priority(SpatialMaterial::RENDER_PRIORITY_MIN); - sm->set_flag(SpatialMaterial::FLAG_DISABLE_DEPTH_TEST, false); - break; - case 2: - // x-ray - sm->set_on_top_of_alpha(); - break; - } - } Ref<SurfaceTool> surface_tool(memnew(SurfaceTool)); @@ -1946,6 +1920,38 @@ void RayCastSpatialGizmoPlugin::redraw(EditorSpatialGizmo *p_gizmo) { ///// +void SpringArmSpatialGizmoPlugin::redraw(EditorSpatialGizmo *p_gizmo) { + + SpringArm *spring_arm = Object::cast_to<SpringArm>(p_gizmo->get_spatial_node()); + + p_gizmo->clear(); + + Vector<Vector3> lines; + + lines.push_back(Vector3()); + lines.push_back(Vector3(0, 0, 1.0) * spring_arm->get_length()); + + Ref<SpatialMaterial> material = get_material("shape_material", p_gizmo); + + p_gizmo->add_lines(lines, material); + p_gizmo->add_collision_segments(lines); +} + +SpringArmSpatialGizmoPlugin::SpringArmSpatialGizmoPlugin() { + Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/shape", Color(0.5, 0.7, 1)); + create_material("shape_material", gizmo_color); +} + +bool SpringArmSpatialGizmoPlugin::has_gizmo(Spatial *p_spatial) { + return Object::cast_to<SpringArm>(p_spatial) != NULL; +} + +String SpringArmSpatialGizmoPlugin::get_name() const { + return "SpringArm"; +} + +///// + VehicleWheelSpatialGizmoPlugin::VehicleWheelSpatialGizmoPlugin() { Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/shape", Color(0.5, 0.7, 1)); diff --git a/editor/spatial_editor_gizmos.h b/editor/spatial_editor_gizmos.h index 877590b91d..6f29e9d999 100644 --- a/editor/spatial_editor_gizmos.h +++ b/editor/spatial_editor_gizmos.h @@ -49,6 +49,7 @@ #include "scene/3d/ray_cast.h" #include "scene/3d/reflection_probe.h" #include "scene/3d/room_instance.h" +#include "scene/3d/spring_arm.h" #include "scene/3d/sprite_3d.h" #include "scene/3d/vehicle_body.h" #include "scene/3d/visibility_notifier.h" @@ -196,6 +197,18 @@ public: RayCastSpatialGizmoPlugin(); }; +class SpringArmSpatialGizmoPlugin : public EditorSpatialGizmoPlugin { + + GDCLASS(SpringArmSpatialGizmoPlugin, EditorSpatialGizmoPlugin); + +public: + bool has_gizmo(Spatial *p_spatial); + String get_name() const; + void redraw(EditorSpatialGizmo *p_gizmo); + + SpringArmSpatialGizmoPlugin(); +}; + class VehicleWheelSpatialGizmoPlugin : public EditorSpatialGizmoPlugin { GDCLASS(VehicleWheelSpatialGizmoPlugin, EditorSpatialGizmoPlugin); @@ -330,14 +343,12 @@ public: }; class CollisionPolygonSpatialGizmoPlugin : public EditorSpatialGizmoPlugin { - GDCLASS(CollisionPolygonSpatialGizmoPlugin, EditorSpatialGizmoPlugin); public: bool has_gizmo(Spatial *p_spatial); String get_name() const; void redraw(EditorSpatialGizmo *p_gizmo); - CollisionPolygonSpatialGizmoPlugin(); }; |