Path: blob/master/editor/animation/animation_blend_tree_editor_plugin.cpp
20914 views
/**************************************************************************/1/* animation_blend_tree_editor_plugin.cpp */2/**************************************************************************/3/* This file is part of: */4/* GODOT ENGINE */5/* https://godotengine.org */6/**************************************************************************/7/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */8/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */9/* */10/* Permission is hereby granted, free of charge, to any person obtaining */11/* a copy of this software and associated documentation files (the */12/* "Software"), to deal in the Software without restriction, including */13/* without limitation the rights to use, copy, modify, merge, publish, */14/* distribute, sublicense, and/or sell copies of the Software, and to */15/* permit persons to whom the Software is furnished to do so, subject to */16/* the following conditions: */17/* */18/* The above copyright notice and this permission notice shall be */19/* included in all copies or substantial portions of the Software. */20/* */21/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */22/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */23/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */24/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */25/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */26/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */27/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */28/**************************************************************************/2930#include "animation_blend_tree_editor_plugin.h"3132#include "core/config/project_settings.h"33#include "core/io/resource_loader.h"34#include "editor/editor_node.h"35#include "editor/editor_string_names.h"36#include "editor/editor_undo_redo_manager.h"37#include "editor/gui/editor_file_dialog.h"38#include "editor/inspector/editor_inspector.h"39#include "editor/inspector/editor_properties.h"40#include "editor/settings/editor_settings.h"41#include "editor/themes/editor_scale.h"42#include "scene/3d/skeleton_3d.h"43#include "scene/gui/check_box.h"44#include "scene/gui/grid_container.h"45#include "scene/gui/line_edit.h"46#include "scene/gui/menu_button.h"47#include "scene/gui/option_button.h"48#include "scene/gui/progress_bar.h"49#include "scene/gui/separator.h"50#include "scene/gui/view_panner.h"51#include "scene/main/window.h"5253void AnimationNodeBlendTreeEditor::add_custom_type(const String &p_name, const Ref<Script> &p_script) {54for (int i = 0; i < add_options.size(); i++) {55ERR_FAIL_COND(add_options[i].script == p_script);56}5758AddOption ao;59ao.name = p_name;60ao.script = p_script;61add_options.push_back(ao);6263_update_options_menu();64}6566void AnimationNodeBlendTreeEditor::remove_custom_type(const Ref<Script> &p_script) {67for (int i = 0; i < add_options.size(); i++) {68if (add_options[i].script == p_script) {69add_options.remove_at(i);70return;71}72}7374_update_options_menu();75}7677void AnimationNodeBlendTreeEditor::_update_options_menu(bool p_has_input_ports) {78add_node->get_popup()->clear();79add_node->get_popup()->reset_size();80for (int i = 0; i < add_options.size(); i++) {81if (p_has_input_ports && add_options[i].input_port_count == 0) {82continue;83}84add_node->get_popup()->add_item(add_options[i].name, i);85}8687Ref<AnimationNode> clipb = EditorSettings::get_singleton()->get_resource_clipboard();88if (clipb.is_valid()) {89add_node->get_popup()->add_separator();90add_node->get_popup()->add_item(TTR("Paste"), MENU_PASTE);91}92add_node->get_popup()->add_separator();93add_node->get_popup()->add_item(TTR("Load..."), MENU_LOAD_FILE);94use_position_from_popup_menu = false;95}9697Size2 AnimationNodeBlendTreeEditor::get_minimum_size() const {98return Size2(10, 200);99}100101void AnimationNodeBlendTreeEditor::_property_changed(const StringName &p_property, const Variant &p_value, const String &p_field, bool p_changing) {102AnimationTree *tree = AnimationTreeEditor::get_singleton()->get_animation_tree();103if (!tree) {104return;105}106updating = true;107EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();108undo_redo->create_action(vformat(TTR("Parameter Changed: %s"), p_property), UndoRedo::MERGE_ENDS);109undo_redo->add_do_property(tree, p_property, p_value);110undo_redo->add_undo_property(tree, p_property, tree->get(p_property));111undo_redo->add_do_method(this, "update_graph");112undo_redo->add_undo_method(this, "update_graph");113undo_redo->commit_action();114updating = false;115}116117void AnimationNodeBlendTreeEditor::update_graph() {118if (updating || blend_tree.is_null()) {119return;120}121122AnimationTree *tree = AnimationTreeEditor::get_singleton()->get_animation_tree();123if (!tree) {124return;125}126127visible_properties.clear();128129// Store selected nodes before clearing the graph.130List<StringName> selected_nodes;131for (int i = 0; i < graph->get_child_count(); i++) {132GraphNode *gn = Object::cast_to<GraphNode>(graph->get_child(i));133if (gn && gn->is_selected()) {134selected_nodes.push_back(gn->get_name());135}136}137138graph->set_scroll_offset(blend_tree->get_graph_offset() * EDSCALE);139140graph->clear_connections();141//erase all nodes142for (int i = 0; i < graph->get_child_count(); i++) {143if (Object::cast_to<GraphNode>(graph->get_child(i))) {144memdelete(graph->get_child(i));145i--;146}147}148149animations.clear();150151LocalVector<StringName> nodes = blend_tree->get_node_list();152153for (const StringName &E : nodes) {154GraphNode *node = memnew(GraphNode);155graph->add_child(node);156157node->set_draggable(!read_only);158159Ref<AnimationNode> agnode = blend_tree->get_node(E);160ERR_CONTINUE(agnode.is_null());161162node->set_position_offset(blend_tree->get_node_position(E) * EDSCALE);163164node->set_title(agnode->get_caption());165node->set_name(E);166167int base = 0;168if (E != SceneStringName(output)) {169LineEdit *name = memnew(LineEdit);170name->set_text(E);171name->set_editable(!read_only);172name->set_expand_to_text_length_enabled(true);173name->set_custom_minimum_size(Vector2(100, 0) * EDSCALE);174node->add_child(name);175node->set_slot(0, false, 0, Color(), true, read_only ? -1 : 0, get_theme_color(SceneStringName(font_color), SNAME("Label")));176name->connect(SceneStringName(text_submitted), callable_mp(this, &AnimationNodeBlendTreeEditor::_node_renamed).bind(agnode), CONNECT_DEFERRED);177name->connect(SceneStringName(focus_exited), callable_mp(this, &AnimationNodeBlendTreeEditor::_node_renamed_focus_out).bind(agnode), CONNECT_DEFERRED);178name->connect(SceneStringName(text_changed), callable_mp(this, &AnimationNodeBlendTreeEditor::_node_rename_lineedit_changed), CONNECT_DEFERRED);179base = 1;180agnode->set_deletable(true);181182if (!read_only) {183Button *delete_button = memnew(Button);184delete_button->set_flat(true);185delete_button->set_focus_mode(FOCUS_ACCESSIBILITY);186delete_button->set_button_icon(get_editor_theme_icon(SNAME("Close")));187delete_button->set_accessibility_name(TTRC("Delete"));188delete_button->connect(SceneStringName(pressed), callable_mp(this, &AnimationNodeBlendTreeEditor::_delete_node_request).bind(E), CONNECT_DEFERRED);189node->get_titlebar_hbox()->add_child(delete_button);190}191}192193for (int i = 0; i < agnode->get_input_count(); i++) {194Label *in_name = memnew(Label);195node->add_child(in_name);196in_name->set_text(agnode->get_input_name(i));197node->set_slot(base + i, true, read_only ? -1 : 0, get_theme_color(SceneStringName(font_color), SNAME("Label")), false, 0, Color());198}199200List<PropertyInfo> pinfo;201agnode->get_parameter_list(&pinfo);202for (const PropertyInfo &F : pinfo) {203if (!(F.usage & PROPERTY_USAGE_EDITOR)) {204continue;205}206String base_path = AnimationTreeEditor::get_singleton()->get_base_path() + String(E) + "/" + F.name;207EditorProperty *prop = EditorInspector::instantiate_property_editor(tree, F.type, base_path, F.hint, F.hint_string, F.usage);208Vector<String> path = F.name.split("/");209float ratio = 0.0f;210if (prop) {211prop->set_read_only(read_only || (F.usage & PROPERTY_USAGE_READ_ONLY));212prop->set_object_and_property(tree, base_path);213if (path.size() >= 2 && path[0] == "conditions") {214prop->set_draw_label(true);215prop->set_label(path[1]);216ratio = 0.9;217}218prop->set_name_split_ratio(ratio);219prop->update_property();220prop->connect("property_changed", callable_mp(this, &AnimationNodeBlendTreeEditor::_property_changed));221222if (F.hint == PROPERTY_HINT_RESOURCE_TYPE) {223// Give the resource editor some more space to make the inside readable.224prop->set_custom_minimum_size(Vector2(180, 0) * EDSCALE);225// Align the size of the node with the resource editor, its un-expanding does not trigger a resize.226prop->connect(SceneStringName(resized), Callable(node, "reset_size"));227}228229node->add_child(prop);230visible_properties.push_back(prop);231}232}233234node->connect("dragged", callable_mp(this, &AnimationNodeBlendTreeEditor::_node_dragged).bind(E));235236if (AnimationTreeEditor::get_singleton()->can_edit(agnode)) {237node->add_child(memnew(HSeparator));238Button *open_in_editor = memnew(Button);239open_in_editor->set_text(TTR("Open Editor"));240open_in_editor->set_button_icon(get_editor_theme_icon(SNAME("Edit")));241node->add_child(open_in_editor);242open_in_editor->connect(SceneStringName(pressed), callable_mp(this, &AnimationNodeBlendTreeEditor::_open_in_editor).bind(E), CONNECT_DEFERRED);243open_in_editor->set_h_size_flags(SIZE_SHRINK_CENTER);244}245246if (agnode->has_filter()) {247node->add_child(memnew(HSeparator));248Button *inspect_filters = memnew(Button);249if (read_only) {250inspect_filters->set_text(TTR("Inspect Filters"));251} else {252inspect_filters->set_text(TTR("Edit Filters"));253}254inspect_filters->set_button_icon(get_editor_theme_icon(SNAME("AnimationFilter")));255node->add_child(inspect_filters);256inspect_filters->connect(SceneStringName(pressed), callable_mp(this, &AnimationNodeBlendTreeEditor::_inspect_filters).bind(E), CONNECT_DEFERRED);257inspect_filters->set_h_size_flags(SIZE_SHRINK_CENTER);258}259260Ref<AnimationNodeAnimation> anim = agnode;261if (anim.is_valid()) {262MenuButton *mb = memnew(MenuButton);263mb->set_text(anim->get_animation());264mb->set_button_icon(get_editor_theme_icon(SNAME("Animation")));265mb->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);266mb->set_disabled(read_only);267Array options;268269node->add_child(memnew(HSeparator));270node->add_child(mb);271272ProgressBar *pb = memnew(ProgressBar);273274List<StringName> anims;275tree->get_animation_list(&anims);276277for (const StringName &F : anims) {278mb->get_popup()->add_item(F);279options.push_back(F);280}281282pb->set_show_percentage(false);283pb->set_custom_minimum_size(Vector2(0, 14) * EDSCALE);284animations[E] = pb;285node->add_child(pb);286287mb->get_popup()->connect("index_pressed", callable_mp(this, &AnimationNodeBlendTreeEditor::_anim_selected).bind(options, E), CONNECT_DEFERRED);288}289290Ref<StyleBox> sb_panel = node->get_theme_stylebox(SceneStringName(panel), "GraphNode")->duplicate();291if (sb_panel.is_valid()) {292sb_panel->set_content_margin(SIDE_TOP, 12 * EDSCALE);293sb_panel->set_content_margin(SIDE_BOTTOM, 12 * EDSCALE);294node->add_theme_style_override(SceneStringName(panel), sb_panel);295}296297node->add_theme_constant_override("separation", 4 * EDSCALE);298}299300List<AnimationNodeBlendTree::NodeConnection> node_connections;301blend_tree->get_node_connections(&node_connections);302303for (const AnimationNodeBlendTree::NodeConnection &E : node_connections) {304StringName from = E.output_node;305StringName to = E.input_node;306int to_idx = E.input_index;307308graph->connect_node(from, 0, to, to_idx);309}310311float graph_minimap_opacity = EDITOR_GET("editors/visual_editors/minimap_opacity");312graph->set_minimap_opacity(graph_minimap_opacity);313float graph_lines_curvature = EDITOR_GET("editors/visual_editors/lines_curvature");314graph->set_connection_lines_curvature(graph_lines_curvature);315316// Restore selected nodes after graph reconstruction.317for (const StringName &name : selected_nodes) {318for (int i = 0; i < graph->get_child_count(); i++) {319GraphNode *gn = Object::cast_to<GraphNode>(graph->get_child(i));320if (gn && gn->get_name() == name) {321gn->set_selected(true);322break;323}324}325}326}327328void AnimationNodeBlendTreeEditor::_file_opened(const String &p_file) {329file_loaded = ResourceLoader::load(p_file);330if (file_loaded.is_valid()) {331_add_node(MENU_LOAD_FILE_CONFIRM);332} else {333EditorNode::get_singleton()->show_warning(TTR("This type of node can't be used. Only animation nodes are allowed."));334}335}336337void AnimationNodeBlendTreeEditor::_add_node(int p_idx) {338Ref<AnimationNode> anode;339340String base_name;341342if (p_idx == MENU_LOAD_FILE) {343open_file->clear_filters();344List<String> ext_filters;345ResourceLoader::get_recognized_extensions_for_type("AnimationNode", &ext_filters);346for (const String &E : ext_filters) {347open_file->add_filter("*." + E);348}349open_file->popup_file_dialog();350return;351} else if (p_idx == MENU_LOAD_FILE_CONFIRM) {352anode = file_loaded;353file_loaded.unref();354base_name = anode->get_class();355} else if (p_idx == MENU_PASTE) {356anode = EditorSettings::get_singleton()->get_resource_clipboard();357ERR_FAIL_COND(anode.is_null());358base_name = anode->get_class();359} else if (!add_options[p_idx].type.is_empty()) {360AnimationNode *an = Object::cast_to<AnimationNode>(ClassDB::instantiate(add_options[p_idx].type));361ERR_FAIL_NULL(an);362anode = Ref<AnimationNode>(an);363base_name = add_options[p_idx].name;364} else {365ERR_FAIL_COND(add_options[p_idx].script.is_null());366StringName base_type = add_options[p_idx].script->get_instance_base_type();367AnimationNode *an = Object::cast_to<AnimationNode>(ClassDB::instantiate(base_type));368ERR_FAIL_NULL(an);369anode = Ref<AnimationNode>(an);370anode->set_script(add_options[p_idx].script);371base_name = add_options[p_idx].name;372}373374Ref<AnimationNodeOutput> out = anode;375if (out.is_valid()) {376EditorNode::get_singleton()->show_warning(TTR("Output node can't be added to the blend tree."));377return;378}379380if (!from_node.is_empty() && anode->get_input_count() == 0) {381from_node = "";382return;383}384385Point2 instance_pos = graph->get_scroll_offset();386if (use_position_from_popup_menu) {387instance_pos += position_from_popup_menu;388} else {389instance_pos += graph->get_size() * 0.5;390}391392instance_pos /= graph->get_zoom();393394int base = 1;395String name = base_name;396while (blend_tree->has_node(name)) {397base++;398name = base_name + " " + itos(base);399}400401EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();402undo_redo->create_action(TTR("Add Node to BlendTree"));403undo_redo->add_do_method(blend_tree.ptr(), "add_node", name, anode, instance_pos / EDSCALE);404undo_redo->add_undo_method(blend_tree.ptr(), "remove_node", name);405406if (!from_node.is_empty()) {407undo_redo->add_do_method(blend_tree.ptr(), "connect_node", name, 0, from_node);408from_node = "";409}410if (!to_node.is_empty() && to_slot != -1) {411undo_redo->add_do_method(blend_tree.ptr(), "connect_node", to_node, to_slot, name);412to_node = "";413to_slot = -1;414}415416undo_redo->add_do_method(this, "update_graph");417undo_redo->add_undo_method(this, "update_graph");418undo_redo->commit_action();419}420421void AnimationNodeBlendTreeEditor::_popup(bool p_has_input_ports, const Vector2 &p_node_position) {422_update_options_menu(p_has_input_ports);423use_position_from_popup_menu = true;424position_from_popup_menu = p_node_position;425add_node->get_popup()->set_position(graph->get_screen_position() + graph->get_local_mouse_position());426add_node->get_popup()->reset_size();427add_node->get_popup()->popup();428}429430void AnimationNodeBlendTreeEditor::_popup_request(const Vector2 &p_position) {431if (read_only) {432return;433}434435_popup(false, p_position);436}437438void AnimationNodeBlendTreeEditor::_connection_to_empty(const String &p_from, int p_from_slot, const Vector2 &p_release_position) {439if (read_only) {440return;441}442443Ref<AnimationNode> node = blend_tree->get_node(p_from);444if (node.is_valid()) {445from_node = p_from;446_popup(true, p_release_position);447}448}449450void AnimationNodeBlendTreeEditor::_connection_from_empty(const String &p_to, int p_to_slot, const Vector2 &p_release_position) {451if (read_only) {452return;453}454455Ref<AnimationNode> node = blend_tree->get_node(p_to);456if (node.is_valid()) {457to_node = p_to;458to_slot = p_to_slot;459_popup(false, p_release_position);460}461}462463void AnimationNodeBlendTreeEditor::_popup_hide() {464to_node = "";465to_slot = -1;466}467468void AnimationNodeBlendTreeEditor::_node_dragged(const Vector2 &p_from, const Vector2 &p_to, const StringName &p_which) {469updating = true;470EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();471undo_redo->create_action(TTR("Node Moved"));472undo_redo->add_do_method(blend_tree.ptr(), "set_node_position", p_which, p_to / EDSCALE);473undo_redo->add_undo_method(blend_tree.ptr(), "set_node_position", p_which, p_from / EDSCALE);474undo_redo->add_do_method(this, "update_graph");475undo_redo->add_undo_method(this, "update_graph");476undo_redo->commit_action();477updating = false;478}479480void AnimationNodeBlendTreeEditor::_connection_request(const String &p_from, int p_from_index, const String &p_to, int p_to_index) {481if (read_only) {482return;483}484485AnimationNodeBlendTree::ConnectionError err = blend_tree->can_connect_node(p_to, p_to_index, p_from);486487if (err == AnimationNodeBlendTree::CONNECTION_ERROR_CONNECTION_EXISTS) {488blend_tree->disconnect_node(p_to, p_to_index);489err = blend_tree->can_connect_node(p_to, p_to_index, p_from);490}491492if (err != AnimationNodeBlendTree::CONNECTION_OK) {493EditorNode::get_singleton()->show_warning(TTR("Unable to connect, port may be in use or connection may be invalid."));494return;495}496497EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();498undo_redo->create_action(TTR("Nodes Connected"));499undo_redo->add_do_method(blend_tree.ptr(), "connect_node", p_to, p_to_index, p_from);500undo_redo->add_undo_method(blend_tree.ptr(), "disconnect_node", p_to, p_to_index);501undo_redo->add_do_method(this, "update_graph");502undo_redo->add_undo_method(this, "update_graph");503undo_redo->commit_action();504}505506void AnimationNodeBlendTreeEditor::_disconnection_request(const String &p_from, int p_from_index, const String &p_to, int p_to_index) {507if (read_only) {508return;509}510511graph->disconnect_node(p_from, p_from_index, p_to, p_to_index);512513updating = true;514EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();515undo_redo->create_action(TTR("Nodes Disconnected"));516undo_redo->add_do_method(blend_tree.ptr(), "disconnect_node", p_to, p_to_index);517undo_redo->add_undo_method(blend_tree.ptr(), "connect_node", p_to, p_to_index, p_from);518undo_redo->add_do_method(this, "update_graph");519undo_redo->add_undo_method(this, "update_graph");520undo_redo->commit_action();521updating = false;522}523524void AnimationNodeBlendTreeEditor::_anim_selected(int p_index, const Array &p_options, const String &p_node) {525String option = p_options[p_index];526527Ref<AnimationNodeAnimation> anim = blend_tree->get_node(p_node);528ERR_FAIL_COND(anim.is_null());529530EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();531undo_redo->create_action(TTR("Set Animation"));532undo_redo->add_do_method(anim.ptr(), "set_animation", option);533undo_redo->add_undo_method(anim.ptr(), "set_animation", anim->get_animation());534undo_redo->add_do_method(this, "update_graph");535undo_redo->add_undo_method(this, "update_graph");536undo_redo->commit_action();537}538539void AnimationNodeBlendTreeEditor::_delete_node_request(const String &p_which) {540if (read_only) {541return;542}543544EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();545undo_redo->create_action(TTR("Delete Node"));546undo_redo->add_do_method(blend_tree.ptr(), "remove_node", p_which);547undo_redo->add_undo_method(blend_tree.ptr(), "add_node", p_which, blend_tree->get_node(p_which), blend_tree.ptr()->get_node_position(p_which));548549List<AnimationNodeBlendTree::NodeConnection> conns;550blend_tree->get_node_connections(&conns);551552for (const AnimationNodeBlendTree::NodeConnection &E : conns) {553if (E.output_node == p_which || E.input_node == p_which) {554undo_redo->add_undo_method(blend_tree.ptr(), "connect_node", E.input_node, E.input_index, E.output_node);555}556}557558undo_redo->add_do_method(this, "update_graph");559undo_redo->add_undo_method(this, "update_graph");560undo_redo->commit_action();561562// Return selection to host BlendTree node.563EditorNode::get_singleton()->push_item(blend_tree.ptr(), "", true);564}565566void AnimationNodeBlendTreeEditor::_delete_nodes_request(const TypedArray<StringName> &p_nodes) {567if (read_only) {568return;569}570571List<StringName> to_erase;572573if (p_nodes.is_empty()) {574for (int i = 0; i < graph->get_child_count(); i++) {575GraphNode *gn = Object::cast_to<GraphNode>(graph->get_child(i));576if (gn && gn->is_selected()) {577Ref<AnimationNode> anode = blend_tree->get_node(gn->get_name());578if (anode->is_deletable()) {579to_erase.push_back(gn->get_name());580}581}582}583} else {584for (int i = 0; i < p_nodes.size(); i++) {585Ref<AnimationNode> anode = blend_tree->get_node(p_nodes[i]);586if (anode->is_deletable()) {587to_erase.push_back(p_nodes[i]);588}589}590}591592if (to_erase.is_empty()) {593return;594}595596EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();597undo_redo->create_action(TTR("Delete Node(s)"));598599for (const StringName &F : to_erase) {600_delete_node_request(F);601}602603undo_redo->commit_action();604}605606void AnimationNodeBlendTreeEditor::_node_selected(Object *p_node) {607if (read_only) {608return;609}610611GraphNode *gn = Object::cast_to<GraphNode>(p_node);612ERR_FAIL_NULL(gn);613614String name = gn->get_name();615616Ref<AnimationNode> anode = blend_tree->get_node(name);617ERR_FAIL_COND(anode.is_null());618619EditorNode::get_singleton()->push_item(anode.ptr(), "", true);620}621622void AnimationNodeBlendTreeEditor::_node_deselected(Object *p_node) {623// Check if no nodes are selected, return selection to host BlendTree node.624bool any_selected = false;625for (int i = 0; i < graph->get_child_count(); i++) {626GraphNode *gn = Object::cast_to<GraphNode>(graph->get_child(i));627if (gn && gn->is_selected()) {628any_selected = true;629break;630}631}632633if (!any_selected) {634EditorNode::get_singleton()->push_item(blend_tree.ptr(), "", true);635}636}637638void AnimationNodeBlendTreeEditor::_open_in_editor(const String &p_which) {639Ref<AnimationNode> an = blend_tree->get_node(p_which);640ERR_FAIL_COND(an.is_null());641AnimationTreeEditor::get_singleton()->enter_editor(p_which);642}643644void AnimationNodeBlendTreeEditor::_filter_toggled() {645updating = true;646EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();647undo_redo->create_action(TTR("Toggle Filter On/Off"));648undo_redo->add_do_method(_filter_edit.ptr(), "set_filter_enabled", filter_enabled->is_pressed());649undo_redo->add_undo_method(_filter_edit.ptr(), "set_filter_enabled", _filter_edit->is_filter_enabled());650undo_redo->add_do_method(this, "_update_filters", _filter_edit);651undo_redo->add_undo_method(this, "_update_filters", _filter_edit);652undo_redo->commit_action();653updating = false;654}655656void AnimationNodeBlendTreeEditor::_filter_edited() {657TreeItem *edited = filters->get_edited();658ERR_FAIL_NULL(edited);659660NodePath edited_path = edited->get_metadata(0);661bool filtered = edited->is_checked(0);662663updating = true;664EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();665undo_redo->create_action(TTR("Change Filter"));666undo_redo->add_do_method(_filter_edit.ptr(), "set_filter_path", edited_path, filtered);667undo_redo->add_undo_method(_filter_edit.ptr(), "set_filter_path", edited_path, _filter_edit->is_path_filtered(edited_path));668undo_redo->add_do_method(this, "_update_filters", _filter_edit);669undo_redo->add_undo_method(this, "_update_filters", _filter_edit);670undo_redo->commit_action();671updating = false;672}673674void AnimationNodeBlendTreeEditor::_filter_fill_selection() {675TreeItem *ti = filters->get_root();676if (!ti) {677return;678}679680updating = true;681EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();682undo_redo->create_action(TTR("Fill Selected Filter Children"));683684_filter_fill_selection_recursive(undo_redo, ti, false);685686undo_redo->add_do_method(this, "_update_filters", _filter_edit);687undo_redo->add_undo_method(this, "_update_filters", _filter_edit);688undo_redo->commit_action();689updating = false;690691_update_filters(_filter_edit);692}693694void AnimationNodeBlendTreeEditor::_filter_invert_selection() {695TreeItem *ti = filters->get_root();696if (!ti) {697return;698}699700updating = true;701EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();702undo_redo->create_action(TTR("Invert Filter Selection"));703704_filter_invert_selection_recursive(undo_redo, ti);705706undo_redo->add_do_method(this, "_update_filters", _filter_edit);707undo_redo->add_undo_method(this, "_update_filters", _filter_edit);708undo_redo->commit_action();709updating = false;710711_update_filters(_filter_edit);712}713714void AnimationNodeBlendTreeEditor::_filter_clear_selection() {715TreeItem *ti = filters->get_root();716if (!ti) {717return;718}719720updating = true;721EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();722undo_redo->create_action(TTR("Clear Filter Selection"));723724_filter_clear_selection_recursive(undo_redo, ti);725726undo_redo->add_do_method(this, "_update_filters", _filter_edit);727undo_redo->add_undo_method(this, "_update_filters", _filter_edit);728undo_redo->commit_action();729updating = false;730731_update_filters(_filter_edit);732}733734void AnimationNodeBlendTreeEditor::_filter_fill_selection_recursive(EditorUndoRedoManager *p_undo_redo, TreeItem *p_item, bool p_parent_filtered) {735TreeItem *ti = p_item->get_first_child();736bool parent_filtered = p_parent_filtered;737while (ti) {738NodePath item_path = ti->get_metadata(0);739bool filtered = _filter_edit->is_path_filtered(item_path);740parent_filtered |= filtered;741742p_undo_redo->add_do_method(_filter_edit.ptr(), "set_filter_path", item_path, parent_filtered);743p_undo_redo->add_undo_method(_filter_edit.ptr(), "set_filter_path", item_path, filtered);744745_filter_fill_selection_recursive(p_undo_redo, ti, parent_filtered);746ti = ti->get_next();747parent_filtered = p_parent_filtered;748}749}750751void AnimationNodeBlendTreeEditor::_filter_invert_selection_recursive(EditorUndoRedoManager *p_undo_redo, TreeItem *p_item) {752TreeItem *ti = p_item->get_first_child();753while (ti) {754NodePath item_path = ti->get_metadata(0);755bool filtered = _filter_edit->is_path_filtered(item_path);756757p_undo_redo->add_do_method(_filter_edit.ptr(), "set_filter_path", item_path, !filtered);758p_undo_redo->add_undo_method(_filter_edit.ptr(), "set_filter_path", item_path, filtered);759760_filter_invert_selection_recursive(p_undo_redo, ti);761ti = ti->get_next();762}763}764765void AnimationNodeBlendTreeEditor::_filter_clear_selection_recursive(EditorUndoRedoManager *p_undo_redo, TreeItem *p_item) {766TreeItem *ti = p_item->get_first_child();767while (ti) {768NodePath item_path = ti->get_metadata(0);769bool filtered = _filter_edit->is_path_filtered(item_path);770771p_undo_redo->add_do_method(_filter_edit.ptr(), "set_filter_path", item_path, false);772p_undo_redo->add_undo_method(_filter_edit.ptr(), "set_filter_path", item_path, filtered);773774_filter_clear_selection_recursive(p_undo_redo, ti);775ti = ti->get_next();776}777}778779bool AnimationNodeBlendTreeEditor::_update_filters(const Ref<AnimationNode> &anode) {780if (updating || _filter_edit != anode) {781return false;782}783784AnimationTree *tree = AnimationTreeEditor::get_singleton()->get_animation_tree();785if (!tree) {786return false;787}788789Node *base = tree->get_node(tree->get_root_node());790if (!base) {791EditorNode::get_singleton()->show_warning(TTR("Animation player has no valid root node path, so unable to retrieve track names."));792return false;793}794795updating = true;796797HashSet<String> paths;798HashMap<String, RBSet<String>> types;799{800List<StringName> animation_list;801tree->get_animation_list(&animation_list);802803for (const StringName &E : animation_list) {804Ref<Animation> anim = tree->get_animation(E);805for (int i = 0; i < anim->get_track_count(); i++) {806String track_path = String(anim->track_get_path(i));807paths.insert(track_path);808809String track_type_name;810Animation::TrackType track_type = anim->track_get_type(i);811switch (track_type) {812case Animation::TrackType::TYPE_ANIMATION: {813track_type_name = TTR("Anim Clips");814} break;815case Animation::TrackType::TYPE_AUDIO: {816track_type_name = TTR("Audio Clips");817} break;818case Animation::TrackType::TYPE_METHOD: {819track_type_name = TTR("Functions");820} break;821default: {822} break;823}824if (!track_type_name.is_empty()) {825types[track_path].insert(track_type_name);826}827}828}829}830831filter_enabled->set_pressed(anode->is_filter_enabled());832filters->clear();833TreeItem *root = filters->create_item();834835HashMap<String, TreeItem *> parenthood;836837for (const String &E : paths) {838NodePath path = E;839TreeItem *ti = nullptr;840String accum;841for (int i = 0; i < path.get_name_count(); i++) {842String name = path.get_name(i);843if (!accum.is_empty()) {844accum += "/";845}846accum += name;847if (!parenthood.has(accum)) {848if (ti) {849ti = filters->create_item(ti);850} else {851ti = filters->create_item(root);852}853parenthood[accum] = ti;854ti->set_text(0, name);855ti->set_selectable(0, false);856ti->set_editable(0, false);857858if (base->has_node(accum)) {859Node *node = base->get_node(accum);860ti->set_icon(0, EditorNode::get_singleton()->get_object_icon(node));861}862863} else {864ti = parenthood[accum];865}866}867868Node *node = nullptr;869if (base->has_node(accum)) {870node = base->get_node(accum);871}872if (!node) {873continue; //no node, can't edit874}875876if (path.get_subname_count()) {877String concat = path.get_concatenated_subnames();878879Skeleton3D *skeleton = Object::cast_to<Skeleton3D>(node);880if (skeleton && skeleton->find_bone(concat) != -1) {881//path in skeleton882const String &bone = concat;883int idx = skeleton->find_bone(bone);884List<String> bone_path;885while (idx != -1) {886bone_path.push_front(skeleton->get_bone_name(idx));887idx = skeleton->get_bone_parent(idx);888}889890accum += ":";891for (List<String>::Element *F = bone_path.front(); F; F = F->next()) {892if (F != bone_path.front()) {893accum += "/";894}895896accum += F->get();897if (!parenthood.has(accum)) {898ti = filters->create_item(ti);899parenthood[accum] = ti;900ti->set_text(0, F->get());901ti->set_selectable(0, false);902ti->set_editable(0, false);903ti->set_icon(0, get_editor_theme_icon(SNAME("Bone")));904} else {905ti = parenthood[accum];906}907}908909ti->set_editable(0, !read_only);910ti->set_selectable(0, true);911ti->set_cell_mode(0, TreeItem::CELL_MODE_CHECK);912ti->set_text(0, concat);913ti->set_checked(0, anode->is_path_filtered(path));914ti->set_icon(0, get_editor_theme_icon(SNAME("Bone")));915ti->set_metadata(0, path);916917} else {918//just a property919ti = filters->create_item(ti);920ti->set_cell_mode(0, TreeItem::CELL_MODE_CHECK);921ti->set_text(0, concat);922ti->set_editable(0, !read_only);923ti->set_selectable(0, true);924ti->set_checked(0, anode->is_path_filtered(path));925ti->set_metadata(0, path);926}927} else {928if (ti) {929//just a node, not a property track930String types_text = "[";931if (types.has(String(path))) {932RBSet<String>::Iterator F = types[String(path)].begin();933types_text += *F;934while (F) {935types_text += " / " + *F;936;937++F;938}939}940types_text += "]";941ti = filters->create_item(ti);942ti->set_cell_mode(0, TreeItem::CELL_MODE_CHECK);943ti->set_text(0, types_text);944ti->set_editable(0, !read_only);945ti->set_selectable(0, true);946ti->set_checked(0, anode->is_path_filtered(path));947ti->set_metadata(0, path);948}949}950}951952updating = false;953954return true;955}956957void AnimationNodeBlendTreeEditor::_inspect_filters(const String &p_which) {958if (read_only) {959filter_dialog->set_title(TTR("Inspect Filtered Tracks:"));960} else {961filter_dialog->set_title(TTR("Edit Filtered Tracks:"));962}963964filter_enabled->set_disabled(read_only);965966Ref<AnimationNode> anode = blend_tree->get_node(p_which);967ERR_FAIL_COND(anode.is_null());968969_filter_edit = anode;970if (!_update_filters(anode)) {971return;972}973974filter_dialog->popup_centered(Size2(500, 500) * EDSCALE);975}976977void AnimationNodeBlendTreeEditor::_update_editor_settings() {978graph->get_panner()->setup((ViewPanner::ControlScheme)EDITOR_GET("editors/panning/sub_editors_panning_scheme").operator int(), ED_GET_SHORTCUT("canvas_item_editor/pan_view"), bool(EDITOR_GET("editors/panning/simple_panning")));979graph->set_warped_panning(EDITOR_GET("editors/panning/warped_mouse_panning"));980}981982void AnimationNodeBlendTreeEditor::_notification(int p_what) {983switch (p_what) {984case NOTIFICATION_READY: {985_update_editor_settings();986} break;987988case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {989if (EditorSettings::get_singleton()->check_changed_settings_in_group("editors/panning")) {990_update_editor_settings();991}992} break;993994case NOTIFICATION_THEME_CHANGED: {995error_panel->add_theme_style_override(SceneStringName(panel), get_theme_stylebox(SceneStringName(panel), SNAME("Tree")));996error_label->add_theme_color_override(SceneStringName(font_color), get_theme_color(SNAME("error_color"), EditorStringName(Editor)));997998if (is_visible_in_tree()) {999update_graph();1000}1001} break;10021003case NOTIFICATION_PROCESS: {1004AnimationTree *tree = AnimationTreeEditor::get_singleton()->get_animation_tree();1005if (!tree) {1006return; // Node has been changed.1007}10081009String error;10101011error = tree->get_editor_error_message();10121013if (error != error_label->get_text()) {1014error_label->set_text(error);1015if (!error.is_empty()) {1016error_panel->show();1017} else {1018error_panel->hide();1019}1020}10211022List<AnimationNodeBlendTree::NodeConnection> conns;1023blend_tree->get_node_connections(&conns);1024for (const AnimationNodeBlendTree::NodeConnection &E : conns) {1025float activity = 0;1026StringName path = AnimationTreeEditor::get_singleton()->get_base_path() + E.input_node;1027if (!tree->is_state_invalid()) {1028activity = tree->get_connection_activity(path, E.input_index);1029}1030graph->set_connection_activity(E.output_node, 0, E.input_node, E.input_index, activity);1031}10321033for (const KeyValue<StringName, ProgressBar *> &E : animations) {1034Ref<AnimationNodeAnimation> an = blend_tree->get_node(E.key);1035if (an.is_valid()) {1036if (tree->has_animation(an->get_animation())) {1037Ref<Animation> anim = tree->get_animation(an->get_animation());1038if (anim.is_valid()) {1039//StringName path = AnimationTreeEditor::get_singleton()->get_base_path() + E.input_node;1040StringName length_path = AnimationTreeEditor::get_singleton()->get_base_path() + String(E.key) + "/current_length";1041StringName time_path = AnimationTreeEditor::get_singleton()->get_base_path() + String(E.key) + "/current_position";1042E.value->set_max(tree->get(length_path));1043E.value->set_value(tree->get(time_path));1044}1045}1046}1047}10481049for (int i = 0; i < visible_properties.size(); i++) {1050visible_properties[i]->update_property();1051}1052} break;10531054case NOTIFICATION_VISIBILITY_CHANGED: {1055set_process(is_visible_in_tree());1056} break;1057}1058}10591060void AnimationNodeBlendTreeEditor::_scroll_changed(const Vector2 &p_scroll) {1061if (read_only) {1062return;1063}10641065if (updating) {1066return;1067}10681069if (blend_tree.is_null()) {1070return;1071}10721073updating = true;1074blend_tree->set_graph_offset(p_scroll / EDSCALE);1075updating = false;1076}10771078void AnimationNodeBlendTreeEditor::_bind_methods() {1079ClassDB::bind_method("update_graph", &AnimationNodeBlendTreeEditor::update_graph);1080ClassDB::bind_method("_update_filters", &AnimationNodeBlendTreeEditor::_update_filters);1081}10821083AnimationNodeBlendTreeEditor *AnimationNodeBlendTreeEditor::singleton = nullptr;10841085// AnimationNode's "node_changed" signal means almost update_input.1086void AnimationNodeBlendTreeEditor::_node_changed(const StringName &p_node_name) {1087// TODO:1088// Here is executed during the commit of EditorNode::undo_redo, it is not possible to create an undo_redo action here.1089// The disconnect when the number of enabled inputs decreases is done in AnimationNodeBlendTree and update_graph().1090// This means that there is no place to register undo_redo actions.1091// In order to implement undo_redo correctly, we may need to implement AnimationNodeEdit such as AnimationTrackKeyEdit1092// and add it to _node_selected() with EditorNode::get_singleton()->push_item(AnimationNodeEdit).1093update_graph();1094}10951096void AnimationNodeBlendTreeEditor::_node_renamed(const String &p_text, Ref<AnimationNode> p_node) {1097if (blend_tree.is_null()) {1098return;1099}11001101AnimationTree *tree = AnimationTreeEditor::get_singleton()->get_animation_tree();1102if (!tree) {1103return;1104}11051106String prev_name = blend_tree->get_node_name(p_node);1107ERR_FAIL_COND(prev_name.is_empty());1108GraphNode *gn = Object::cast_to<GraphNode>(graph->get_node(prev_name));1109ERR_FAIL_NULL(gn);11101111const String &new_name = p_text;11121113ERR_FAIL_COND(new_name.is_empty() || new_name.contains_char('.') || new_name.contains_char('/'));11141115if (new_name == prev_name) {1116return; //nothing to do1117}11181119const String &base_name = new_name;1120int base = 1;1121String name = base_name;1122while (blend_tree->has_node(name)) {1123base++;1124name = base_name + " " + itos(base);1125}11261127String base_path = AnimationTreeEditor::get_singleton()->get_base_path();11281129updating = true;1130EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();1131undo_redo->create_action(TTR("Node Renamed"));1132undo_redo->add_do_method(blend_tree.ptr(), "rename_node", prev_name, name);1133undo_redo->add_undo_method(blend_tree.ptr(), "rename_node", name, prev_name);1134undo_redo->add_do_method(this, "update_graph");1135undo_redo->add_undo_method(this, "update_graph");1136undo_redo->commit_action();1137updating = false;1138gn->set_name(new_name);1139gn->set_size(gn->get_minimum_size());11401141//change editors accordingly1142for (int i = 0; i < visible_properties.size(); i++) {1143String pname = visible_properties[i]->get_edited_property().operator String();1144if (pname.begins_with(base_path + prev_name)) {1145String new_name2 = pname.replace_first(base_path + prev_name, base_path + name);1146visible_properties[i]->set_object_and_property(visible_properties[i]->get_edited_object(), new_name2);1147}1148}11491150//recreate connections1151graph->clear_connections();11521153List<AnimationNodeBlendTree::NodeConnection> node_connections;1154blend_tree->get_node_connections(&node_connections);11551156for (const AnimationNodeBlendTree::NodeConnection &E : node_connections) {1157StringName from = E.output_node;1158StringName to = E.input_node;1159int to_idx = E.input_index;11601161graph->connect_node(from, 0, to, to_idx);1162}11631164//update animations1165for (const KeyValue<StringName, ProgressBar *> &E : animations) {1166if (E.key == prev_name) {1167animations[new_name] = animations[prev_name];1168animations.erase(prev_name);1169break;1170}1171}11721173update_graph(); // Needed to update the signal connections with the new name.1174current_node_rename_text = String();1175}11761177void AnimationNodeBlendTreeEditor::_node_renamed_focus_out(Ref<AnimationNode> p_node) {1178if (current_node_rename_text.is_empty()) {1179return; // The text_submitted signal triggered the graph update and freed the LineEdit.1180}1181_node_renamed(current_node_rename_text, p_node);1182}11831184void AnimationNodeBlendTreeEditor::_node_rename_lineedit_changed(const String &p_text) {1185current_node_rename_text = p_text;1186}11871188bool AnimationNodeBlendTreeEditor::can_edit(const Ref<AnimationNode> &p_node) {1189Ref<AnimationNodeBlendTree> bt = p_node;1190return bt.is_valid();1191}11921193void AnimationNodeBlendTreeEditor::edit(const Ref<AnimationNode> &p_node) {1194if (blend_tree.is_valid()) {1195blend_tree->disconnect("node_changed", callable_mp(this, &AnimationNodeBlendTreeEditor::_node_changed));1196}11971198blend_tree = p_node;11991200read_only = false;12011202if (blend_tree.is_null()) {1203hide();1204} else {1205read_only = EditorNode::get_singleton()->is_resource_read_only(blend_tree);12061207blend_tree->connect("node_changed", callable_mp(this, &AnimationNodeBlendTreeEditor::_node_changed));12081209update_graph();1210}12111212add_node->set_disabled(read_only);1213graph->set_show_arrange_button(!read_only);1214}12151216AnimationNodeBlendTreeEditor::AnimationNodeBlendTreeEditor() {1217singleton = this;1218updating = false;1219use_position_from_popup_menu = false;12201221graph = memnew(GraphEdit);1222add_child(graph);1223graph->add_valid_right_disconnect_type(0);1224graph->add_valid_left_disconnect_type(0);1225graph->set_v_size_flags(SIZE_EXPAND_FILL);1226graph->connect("connection_request", callable_mp(this, &AnimationNodeBlendTreeEditor::_connection_request), CONNECT_DEFERRED);1227graph->connect("disconnection_request", callable_mp(this, &AnimationNodeBlendTreeEditor::_disconnection_request), CONNECT_DEFERRED);1228graph->connect("node_selected", callable_mp(this, &AnimationNodeBlendTreeEditor::_node_selected));1229graph->connect("node_deselected", callable_mp(this, &AnimationNodeBlendTreeEditor::_node_deselected));1230graph->connect("scroll_offset_changed", callable_mp(this, &AnimationNodeBlendTreeEditor::_scroll_changed));1231graph->connect("delete_nodes_request", callable_mp(this, &AnimationNodeBlendTreeEditor::_delete_nodes_request));1232graph->connect("popup_request", callable_mp(this, &AnimationNodeBlendTreeEditor::_popup_request));1233graph->connect("connection_to_empty", callable_mp(this, &AnimationNodeBlendTreeEditor::_connection_to_empty));1234graph->connect("connection_from_empty", callable_mp(this, &AnimationNodeBlendTreeEditor::_connection_from_empty));1235float graph_minimap_opacity = EDITOR_GET("editors/visual_editors/minimap_opacity");1236graph->set_minimap_opacity(graph_minimap_opacity);1237float graph_lines_curvature = EDITOR_GET("editors/visual_editors/lines_curvature");1238graph->set_connection_lines_curvature(graph_lines_curvature);12391240VSeparator *vs = memnew(VSeparator);1241graph->get_menu_hbox()->add_child(vs);1242graph->get_menu_hbox()->move_child(vs, 0);12431244add_node = memnew(MenuButton);1245graph->get_menu_hbox()->add_child(add_node);1246add_node->set_text(TTR("Add Node..."));1247graph->get_menu_hbox()->move_child(add_node, 0);1248add_node->get_popup()->connect(SceneStringName(id_pressed), callable_mp(this, &AnimationNodeBlendTreeEditor::_add_node));1249add_node->get_popup()->connect("popup_hide", callable_mp(this, &AnimationNodeBlendTreeEditor::_popup_hide), CONNECT_DEFERRED);1250add_node->connect("about_to_popup", callable_mp(this, &AnimationNodeBlendTreeEditor::_update_options_menu).bind(false));1251add_node->set_disabled(read_only);12521253add_options.push_back(AddOption("Animation", "AnimationNodeAnimation"));1254add_options.push_back(AddOption("OneShot", "AnimationNodeOneShot", 2));1255add_options.push_back(AddOption("Add2", "AnimationNodeAdd2", 2));1256add_options.push_back(AddOption("Add3", "AnimationNodeAdd3", 3));1257add_options.push_back(AddOption("Blend2", "AnimationNodeBlend2", 2));1258add_options.push_back(AddOption("Blend3", "AnimationNodeBlend3", 3));1259add_options.push_back(AddOption("Sub2", "AnimationNodeSub2", 2));1260add_options.push_back(AddOption("TimeSeek", "AnimationNodeTimeSeek", 1));1261add_options.push_back(AddOption("TimeScale", "AnimationNodeTimeScale", 1));1262add_options.push_back(AddOption("Transition", "AnimationNodeTransition"));1263add_options.push_back(AddOption("BlendTree", "AnimationNodeBlendTree"));1264add_options.push_back(AddOption("BlendSpace1D", "AnimationNodeBlendSpace1D"));1265add_options.push_back(AddOption("BlendSpace2D", "AnimationNodeBlendSpace2D"));1266add_options.push_back(AddOption("StateMachine", "AnimationNodeStateMachine"));1267_update_options_menu();12681269error_panel = memnew(PanelContainer);1270add_child(error_panel);1271error_label = memnew(Label);1272error_label->set_focus_mode(FOCUS_ACCESSIBILITY);1273error_panel->add_child(error_label);1274error_label->set_text("eh");12751276filter_dialog = memnew(AcceptDialog);1277add_child(filter_dialog);1278filter_dialog->set_title(TTR("Edit Filtered Tracks:"));12791280VBoxContainer *filter_vbox = memnew(VBoxContainer);1281filter_dialog->add_child(filter_vbox);12821283HBoxContainer *filter_hbox = memnew(HBoxContainer);1284filter_vbox->add_child(filter_hbox);12851286filter_enabled = memnew(CheckBox);1287filter_enabled->set_text(TTR("Enable Filtering"));1288filter_enabled->connect(SceneStringName(pressed), callable_mp(this, &AnimationNodeBlendTreeEditor::_filter_toggled));1289filter_hbox->add_child(filter_enabled);12901291filter_fill_selection = memnew(Button);1292filter_fill_selection->set_text(TTR("Fill Selected Children"));1293filter_fill_selection->connect(SceneStringName(pressed), callable_mp(this, &AnimationNodeBlendTreeEditor::_filter_fill_selection));1294filter_hbox->add_child(filter_fill_selection);12951296filter_invert_selection = memnew(Button);1297filter_invert_selection->set_text(TTR("Invert"));1298filter_invert_selection->connect(SceneStringName(pressed), callable_mp(this, &AnimationNodeBlendTreeEditor::_filter_invert_selection));1299filter_hbox->add_child(filter_invert_selection);13001301filter_clear_selection = memnew(Button);1302filter_clear_selection->set_text(TTR("Clear"));1303filter_clear_selection->connect(SceneStringName(pressed), callable_mp(this, &AnimationNodeBlendTreeEditor::_filter_clear_selection));1304filter_hbox->add_child(filter_clear_selection);13051306filters = memnew(Tree);1307filter_vbox->add_child(filters);1308filters->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);1309filters->set_v_size_flags(SIZE_EXPAND_FILL);1310filters->set_hide_root(true);1311filters->connect("item_edited", callable_mp(this, &AnimationNodeBlendTreeEditor::_filter_edited));13121313open_file = memnew(EditorFileDialog);1314add_child(open_file);1315open_file->set_title(TTR("Open Animation Node"));1316open_file->set_file_mode(EditorFileDialog::FILE_MODE_OPEN_FILE);1317open_file->connect("file_selected", callable_mp(this, &AnimationNodeBlendTreeEditor::_file_opened));13181319animation_node_inspector_plugin.instantiate();1320EditorInspector::add_inspector_plugin(animation_node_inspector_plugin);1321}13221323// EditorPluginAnimationNodeAnimation13241325void AnimationNodeAnimationEditor::_open_set_custom_timeline_from_marker_dialog() {1326AnimationTree *tree = AnimationTreeEditor::get_singleton()->get_animation_tree();1327StringName anim_name = animation_node_animation->get_animation();1328PackedStringArray markers = tree->has_animation(anim_name) ? tree->get_animation(anim_name)->get_marker_names() : PackedStringArray();13291330dialog->select_start->clear();1331dialog->select_start->add_icon_item(get_editor_theme_icon(SNAME("PlayStart")), TTR("Start of Animation"));1332dialog->select_start->add_separator();1333dialog->select_end->clear();1334dialog->select_end->add_icon_item(get_editor_theme_icon(SNAME("PlayStartBackwards")), TTR("End of Animation"));1335dialog->select_end->add_separator();13361337for (const String &marker : markers) {1338dialog->select_start->add_item(marker);1339dialog->select_end->add_item(marker);1340}13411342// Because the default selections are always valid, and marker times won't change during the dialog, we can ensure that the user can only select valid markers.1343// This invariant is maintained by _validate_markers.1344dialog->select_start->select(0);1345dialog->select_end->select(0);13461347dialog->popup_centered(Size2(200, 0) * EDSCALE);1348}13491350void AnimationNodeAnimationEditor::_validate_markers(int p_id) {1351// Note: p_id is ignored. It is included because OptionButton's item_changed signal always passes it.1352int start_id = dialog->select_start->get_selected_id();1353int end_id = dialog->select_end->get_selected_id();13541355StringName anim_name = animation_node_animation->get_animation();1356Ref<Animation> animation = AnimationTreeEditor::get_singleton()->get_animation_tree()->get_animation(anim_name);1357ERR_FAIL_COND(animation.is_null());13581359double start_time = start_id < 2 ? 0 : animation->get_marker_time(dialog->select_start->get_item_text(start_id));1360double end_time = end_id < 2 ? animation->get_length() : animation->get_marker_time(dialog->select_end->get_item_text(end_id));13611362// p_start and p_end have the same item count.1363for (int i = 2; i < dialog->select_start->get_item_count(); i++) {1364String start_marker = dialog->select_start->get_item_text(i);1365String end_marker = dialog->select_end->get_item_text(i);1366dialog->select_start->set_item_disabled(i, end_id >= 2 && (i == end_id || animation->get_marker_time(start_marker) > end_time));1367dialog->select_end->set_item_disabled(i, start_id >= 2 && (i == start_id || start_time > animation->get_marker_time(end_marker)));1368}1369}13701371void AnimationNodeAnimationEditor::_confirm_set_custom_timeline_from_marker_dialog() {1372int start_id = dialog->select_start->get_selected_id();1373int end_id = dialog->select_end->get_selected_id();13741375Ref<Animation> animation = AnimationTreeEditor::get_singleton()->get_animation_tree()->get_animation(animation_node_animation->get_animation());1376ERR_FAIL_COND(animation.is_null());1377double start_time = start_id < 2 ? 0 : animation->get_marker_time(dialog->select_start->get_item_text(start_id));1378double end_time = end_id < 2 ? animation->get_length() : animation->get_marker_time(dialog->select_end->get_item_text(end_id));1379double length = end_time - start_time;13801381EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();1382undo_redo->create_action(TTR("Set Custom Timeline from Marker"));1383undo_redo->add_do_method(*animation_node_animation, "set_start_offset", start_time);1384undo_redo->add_undo_method(*animation_node_animation, "set_start_offset", animation_node_animation->get_start_offset());1385undo_redo->add_do_method(*animation_node_animation, "set_stretch_time_scale", false);1386undo_redo->add_undo_method(*animation_node_animation, "set_stretch_time_scale", animation_node_animation->is_stretching_time_scale());1387undo_redo->add_do_method(*animation_node_animation, "set_timeline_length", length);1388undo_redo->add_undo_method(*animation_node_animation, "set_timeline_length", animation_node_animation->get_timeline_length());1389undo_redo->add_do_method(*animation_node_animation, "notify_property_list_changed");1390undo_redo->add_undo_method(*animation_node_animation, "notify_property_list_changed");1391undo_redo->commit_action();1392}13931394AnimationNodeAnimationEditor::AnimationNodeAnimationEditor(Ref<AnimationNodeAnimation> p_animation_node_animation) {1395animation_node_animation = p_animation_node_animation;13961397dialog = memnew(AnimationNodeAnimationEditorDialog);1398add_child(dialog);1399dialog->set_hide_on_ok(false);1400dialog->select_start->connect(SceneStringName(item_selected), callable_mp(this, &AnimationNodeAnimationEditor::_validate_markers));1401dialog->select_end->connect(SceneStringName(item_selected), callable_mp(this, &AnimationNodeAnimationEditor::_validate_markers));1402dialog->connect(SceneStringName(confirmed), callable_mp(this, &AnimationNodeAnimationEditor::_confirm_set_custom_timeline_from_marker_dialog));14031404Control *top_spacer = memnew(Control);1405add_child(top_spacer);1406top_spacer->set_custom_minimum_size(Size2(0, 2) * EDSCALE);14071408button = memnew(Button);1409add_child(button);1410button->set_text(TTR("Set Custom Timeline from Marker"));1411button->set_h_size_flags(Control::SIZE_SHRINK_CENTER);1412button->connect(SceneStringName(pressed), callable_mp(this, &AnimationNodeAnimationEditor::_open_set_custom_timeline_from_marker_dialog));14131414Control *bottom_spacer = memnew(Control);1415add_child(bottom_spacer);1416bottom_spacer->set_custom_minimum_size(Size2(0, 2) * EDSCALE);1417}14181419void AnimationNodeAnimationEditor::_notification(int p_what) {1420switch (p_what) {1421case NOTIFICATION_THEME_CHANGED: {1422button->set_theme_type_variation(SNAME("InspectorActionButton"));1423button->set_button_icon(get_editor_theme_icon(SNAME("Edit")));1424} break;1425}1426}14271428bool EditorInspectorPluginAnimationNodeAnimation::can_handle(Object *p_object) {1429Ref<AnimationNodeAnimation> ana(Object::cast_to<AnimationNodeAnimation>(p_object));1430return ana.is_valid() && ana->is_using_custom_timeline();1431}14321433bool EditorInspectorPluginAnimationNodeAnimation::parse_property(Object *p_object, const Variant::Type p_type, const String &p_path, const PropertyHint p_hint, const String &p_hint_text, const BitField<PropertyUsageFlags> p_usage, const bool p_wide) {1434Ref<AnimationNodeAnimation> ana(Object::cast_to<AnimationNodeAnimation>(p_object));1435ERR_FAIL_COND_V(ana.is_null(), false);14361437if (p_path == "timeline_length") {1438add_custom_control(memnew(AnimationNodeAnimationEditor(ana)));1439}14401441return false;1442}14431444AnimationNodeAnimationEditorDialog::AnimationNodeAnimationEditorDialog() {1445set_title(TTR("Select Markers"));14461447GridContainer *grid = memnew(GridContainer);1448grid->set_columns(2);1449grid->set_offsets_preset(Control::PRESET_FULL_RECT);1450add_child(grid);14511452Label *label_start = memnew(Label(TTR("Start Marker")));1453grid->add_child(label_start);1454label_start->set_h_size_flags(Control::SIZE_EXPAND_FILL);1455label_start->set_stretch_ratio(1);1456select_start = memnew(OptionButton);1457select_start->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);1458select_start->set_accessibility_name(TTRC("Start Marker"));1459grid->add_child(select_start);1460select_start->set_h_size_flags(Control::SIZE_EXPAND_FILL);1461select_start->set_stretch_ratio(2);14621463Label *label_end = memnew(Label(TTR("End Marker")));1464grid->add_child(label_end);1465label_end->set_h_size_flags(Control::SIZE_EXPAND_FILL);1466label_end->set_stretch_ratio(1);1467select_end = memnew(OptionButton);1468select_end->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);1469select_end->set_accessibility_name(TTRC("End Marker"));1470grid->add_child(select_end);1471select_end->set_h_size_flags(Control::SIZE_EXPAND_FILL);1472select_end->set_stretch_ratio(2);1473}147414751476