|
@@ -50,6 +50,7 @@
|
|
|
#include "scene/gui/tree.h"
|
|
|
#include "scene/main/viewport.h"
|
|
|
#include "scene/main/window.h"
|
|
|
+#include "scene/scene_string_names.h"
|
|
|
|
|
|
bool AnimationNodeStateMachineEditor::can_edit(const Ref<AnimationNode> &p_node) {
|
|
|
Ref<AnimationNodeStateMachine> ansm = p_node;
|
|
@@ -67,7 +68,6 @@ void AnimationNodeStateMachineEditor::edit(const Ref<AnimationNode> &p_node) {
|
|
|
selected_transition_from = StringName();
|
|
|
selected_transition_to = StringName();
|
|
|
selected_transition_index = -1;
|
|
|
- selected_multi_transition = TransitionLine();
|
|
|
selected_node = StringName();
|
|
|
selected_nodes.clear();
|
|
|
_update_mode();
|
|
@@ -78,14 +78,60 @@ void AnimationNodeStateMachineEditor::edit(const Ref<AnimationNode> &p_node) {
|
|
|
tool_connect->set_disabled(read_only);
|
|
|
}
|
|
|
|
|
|
+String AnimationNodeStateMachineEditor::_get_root_playback_path(String &r_node_directory) {
|
|
|
+ AnimationTree *tree = AnimationTreeEditor::get_singleton()->get_animation_tree();
|
|
|
+ Vector<String> edited_path = AnimationTreeEditor::get_singleton()->get_edited_path();
|
|
|
+
|
|
|
+ String base_path;
|
|
|
+ Vector<String> node_directory_path;
|
|
|
+
|
|
|
+ bool is_playable_anodesm_found = false;
|
|
|
+
|
|
|
+ if (edited_path.size()) {
|
|
|
+ while (!is_playable_anodesm_found) {
|
|
|
+ base_path = String("/").join(edited_path);
|
|
|
+ Ref<AnimationNodeStateMachine> anodesm = !edited_path.size() ? tree->get_tree_root() : tree->get_tree_root()->find_node_by_path(base_path);
|
|
|
+ if (!anodesm.is_valid()) {
|
|
|
+ break;
|
|
|
+ } else {
|
|
|
+ if (anodesm->get_state_machine_type() != AnimationNodeStateMachine::STATE_MACHINE_TYPE_GROUPED) {
|
|
|
+ is_playable_anodesm_found = true;
|
|
|
+ } else {
|
|
|
+ int idx = edited_path.size() - 1;
|
|
|
+ node_directory_path.push_back(edited_path[idx]);
|
|
|
+ edited_path.remove_at(idx);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (is_playable_anodesm_found) {
|
|
|
+ // Return Root/Nested state machine playback.
|
|
|
+ node_directory_path.reverse();
|
|
|
+ r_node_directory = String("/").join(node_directory_path);
|
|
|
+ if (node_directory_path.size()) {
|
|
|
+ r_node_directory += "/";
|
|
|
+ }
|
|
|
+ base_path = !edited_path.size() ? String(SceneStringNames::get_singleton()->parameters_base_path) + "playback" : String(SceneStringNames::get_singleton()->parameters_base_path) + base_path + "/playback";
|
|
|
+ } else {
|
|
|
+ // Hmmm, we have to return Grouped state machine playback...
|
|
|
+ // It will give the user the error that Root/Nested state machine should be retrieved, that would be kind :-)
|
|
|
+ r_node_directory = String();
|
|
|
+ base_path = AnimationTreeEditor::get_singleton()->get_base_path() + "playback";
|
|
|
+ }
|
|
|
+
|
|
|
+ return base_path;
|
|
|
+}
|
|
|
+
|
|
|
void AnimationNodeStateMachineEditor::_state_machine_gui_input(const Ref<InputEvent> &p_event) {
|
|
|
AnimationTree *tree = AnimationTreeEditor::get_singleton()->get_animation_tree();
|
|
|
if (!tree) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- Ref<AnimationNodeStateMachinePlayback> playback = tree->get(AnimationTreeEditor::get_singleton()->get_base_path() + "playback");
|
|
|
- if (playback.is_null()) {
|
|
|
+ String node_directory;
|
|
|
+ Ref<AnimationNodeStateMachinePlayback> playback = tree->get(_get_root_playback_path(node_directory));
|
|
|
+ if (!playback.is_valid()) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
@@ -99,16 +145,6 @@ void AnimationNodeStateMachineEditor::_state_machine_gui_input(const Ref<InputEv
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- // Group selected nodes on a state machine
|
|
|
- if (tool_select->is_pressed() && k.is_valid() && k->is_pressed() && k->is_ctrl_pressed() && !k->is_shift_pressed() && k->get_keycode() == Key::G && !k->is_echo()) {
|
|
|
- _group_selected_nodes();
|
|
|
- }
|
|
|
-
|
|
|
- // Ungroup state machine
|
|
|
- if (tool_select->is_pressed() && k.is_valid() && k->is_pressed() && k->is_ctrl_pressed() && k->is_shift_pressed() && k->get_keycode() == Key::G && !k->is_echo()) {
|
|
|
- _ungroup_selected_nodes();
|
|
|
- }
|
|
|
-
|
|
|
Ref<InputEventMouseButton> mb = p_event;
|
|
|
|
|
|
// Add new node
|
|
@@ -124,17 +160,16 @@ void AnimationNodeStateMachineEditor::_state_machine_gui_input(const Ref<InputEv
|
|
|
selected_transition_from = StringName();
|
|
|
selected_transition_to = StringName();
|
|
|
selected_transition_index = -1;
|
|
|
- selected_multi_transition = TransitionLine();
|
|
|
selected_node = StringName();
|
|
|
|
|
|
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 || !playback->is_playing()) {
|
|
|
- //start
|
|
|
- playback->start(node_rects[i].node_name);
|
|
|
+ // Start
|
|
|
+ playback->start(node_directory + String(node_rects[i].node_name));
|
|
|
} else {
|
|
|
- //travel
|
|
|
- playback->travel(node_rects[i].node_name);
|
|
|
+ // Travel
|
|
|
+ playback->travel(node_directory + String(node_rects[i].node_name));
|
|
|
}
|
|
|
state_machine_draw->queue_redraw();
|
|
|
return;
|
|
@@ -213,26 +248,11 @@ void AnimationNodeStateMachineEditor::_state_machine_gui_input(const Ref<InputEv
|
|
|
selected_transition_index = closest;
|
|
|
|
|
|
Ref<AnimationNodeStateMachineTransition> tr = state_machine->get_transition(closest);
|
|
|
- EditorNode::get_singleton()->push_item(tr.ptr(), "", true);
|
|
|
-
|
|
|
- if (!transition_lines[closest].multi_transitions.is_empty()) {
|
|
|
- selected_transition_index = -1;
|
|
|
- selected_multi_transition = transition_lines[closest];
|
|
|
-
|
|
|
- Ref<EditorAnimationMultiTransitionEdit> multi;
|
|
|
- multi.instantiate();
|
|
|
- multi->add_transition(selected_transition_from, selected_transition_to, tr);
|
|
|
-
|
|
|
- for (int i = 0; i < transition_lines[closest].multi_transitions.size(); i++) {
|
|
|
- int index = transition_lines[closest].multi_transitions[i].transition_index;
|
|
|
-
|
|
|
- Ref<AnimationNodeStateMachineTransition> transition = state_machine->get_transition(index);
|
|
|
- StringName from = transition_lines[closest].multi_transitions[i].from_node;
|
|
|
- StringName to = transition_lines[closest].multi_transitions[i].to_node;
|
|
|
-
|
|
|
- multi->add_transition(from, to, transition);
|
|
|
- }
|
|
|
- EditorNode::get_singleton()->push_item(multi.ptr(), "", true);
|
|
|
+ if (!state_machine->is_transition_across_group(closest)) {
|
|
|
+ EditorNode::get_singleton()->push_item(tr.ptr(), "", true);
|
|
|
+ } else {
|
|
|
+ EditorNode::get_singleton()->push_item(tr.ptr(), "", true);
|
|
|
+ EditorNode::get_singleton()->push_item(nullptr, "", true);
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -296,11 +316,7 @@ void AnimationNodeStateMachineEditor::_state_machine_gui_input(const Ref<InputEv
|
|
|
EditorNode::get_singleton()->show_warning(TTR("Transition exists!"));
|
|
|
connecting = false;
|
|
|
} else {
|
|
|
- if (anodesm.is_valid() || end_node.is_valid()) {
|
|
|
- _open_connect_menu(mb->get_position());
|
|
|
- } else {
|
|
|
- _add_transition();
|
|
|
- }
|
|
|
+ _add_transition();
|
|
|
}
|
|
|
} else {
|
|
|
_open_menu(mb->get_position());
|
|
@@ -481,12 +497,6 @@ void AnimationNodeStateMachineEditor::_state_machine_gui_input(const Ref<InputEv
|
|
|
String from = String(transition_lines[closest].from_node);
|
|
|
String to = String(transition_lines[closest].to_node);
|
|
|
String tooltip = from + " -> " + to;
|
|
|
-
|
|
|
- for (int i = 0; i < transition_lines[closest].multi_transitions.size(); i++) {
|
|
|
- from = String(transition_lines[closest].multi_transitions[i].from_node);
|
|
|
- to = String(transition_lines[closest].multi_transitions[i].to_node);
|
|
|
- tooltip += "\n" + from + " -> " + to;
|
|
|
- }
|
|
|
state_machine_draw->set_tooltip_text(tooltip);
|
|
|
} else {
|
|
|
state_machine_draw->set_tooltip_text("");
|
|
@@ -522,224 +532,6 @@ Control::CursorShape AnimationNodeStateMachineEditor::get_cursor_shape(const Poi
|
|
|
return cursor_shape;
|
|
|
}
|
|
|
|
|
|
-void AnimationNodeStateMachineEditor::_group_selected_nodes() {
|
|
|
- if (!selected_nodes.is_empty()) {
|
|
|
- if (selected_nodes.size() == 1 && (*selected_nodes.begin() == state_machine->start_node || *selected_nodes.begin() == state_machine->end_node))
|
|
|
- return;
|
|
|
-
|
|
|
- Ref<AnimationNodeStateMachine> group_sm = memnew(AnimationNodeStateMachine);
|
|
|
- Vector2 group_position;
|
|
|
-
|
|
|
- Vector<NodeUR> nodes_ur;
|
|
|
- Vector<TransitionUR> transitions_ur;
|
|
|
-
|
|
|
- int base = 1;
|
|
|
- String base_name = group_sm->get_caption();
|
|
|
- String group_name = base_name;
|
|
|
-
|
|
|
- while (state_machine->has_node(group_name) && !selected_nodes.has(group_name)) {
|
|
|
- base++;
|
|
|
- group_name = base_name + " " + itos(base);
|
|
|
- }
|
|
|
-
|
|
|
- updating = true;
|
|
|
- EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
|
|
|
- undo_redo->create_action("Group");
|
|
|
-
|
|
|
- // Move selected nodes to the new state machine
|
|
|
- for (const StringName &E : selected_nodes) {
|
|
|
- if (!state_machine->can_edit_node(E)) {
|
|
|
- continue;
|
|
|
- }
|
|
|
-
|
|
|
- Ref<AnimationNode> node = state_machine->get_node(E);
|
|
|
- Vector2 node_position = state_machine->get_node_position(E);
|
|
|
- group_position += node_position;
|
|
|
-
|
|
|
- NodeUR new_node;
|
|
|
- new_node.name = E;
|
|
|
- new_node.node = node;
|
|
|
- new_node.position = node_position;
|
|
|
-
|
|
|
- nodes_ur.push_back(new_node);
|
|
|
- }
|
|
|
-
|
|
|
- // Add the transitions to the new state machine
|
|
|
- for (int i = 0; i < state_machine->get_transition_count(); i++) {
|
|
|
- String from = state_machine->get_transition_from(i);
|
|
|
- String to = state_machine->get_transition_to(i);
|
|
|
-
|
|
|
- String local_from = from.get_slicec('/', 0);
|
|
|
- String local_to = to.get_slicec('/', 0);
|
|
|
-
|
|
|
- String old_from = from;
|
|
|
- String old_to = to;
|
|
|
-
|
|
|
- bool from_selected = false;
|
|
|
- bool to_selected = false;
|
|
|
-
|
|
|
- if (selected_nodes.has(local_from) && local_from != state_machine->start_node) {
|
|
|
- from_selected = true;
|
|
|
- }
|
|
|
- if (selected_nodes.has(local_to) && local_to != state_machine->end_node) {
|
|
|
- to_selected = true;
|
|
|
- }
|
|
|
- if (!from_selected && !to_selected) {
|
|
|
- continue;
|
|
|
- }
|
|
|
-
|
|
|
- Ref<AnimationNodeStateMachineTransition> tr = state_machine->get_transition(i);
|
|
|
-
|
|
|
- if (!from_selected) {
|
|
|
- from = "../" + old_from;
|
|
|
- }
|
|
|
- if (!to_selected) {
|
|
|
- to = "../" + old_to;
|
|
|
- }
|
|
|
-
|
|
|
- TransitionUR new_tr;
|
|
|
- new_tr.new_from = from;
|
|
|
- new_tr.new_to = to;
|
|
|
- new_tr.old_from = old_from;
|
|
|
- new_tr.old_to = old_to;
|
|
|
- new_tr.transition = tr;
|
|
|
-
|
|
|
- transitions_ur.push_back(new_tr);
|
|
|
- }
|
|
|
-
|
|
|
- for (int i = 0; i < nodes_ur.size(); i++) {
|
|
|
- undo_redo->add_do_method(state_machine.ptr(), "remove_node", nodes_ur[i].name);
|
|
|
- undo_redo->add_undo_method(group_sm.ptr(), "remove_node", nodes_ur[i].name);
|
|
|
- }
|
|
|
-
|
|
|
- undo_redo->add_do_method(state_machine.ptr(), "add_node", group_name, group_sm, group_position / nodes_ur.size());
|
|
|
- undo_redo->add_undo_method(state_machine.ptr(), "remove_node", group_name);
|
|
|
-
|
|
|
- for (int i = 0; i < nodes_ur.size(); i++) {
|
|
|
- undo_redo->add_do_method(group_sm.ptr(), "add_node", nodes_ur[i].name, nodes_ur[i].node, nodes_ur[i].position);
|
|
|
- undo_redo->add_undo_method(state_machine.ptr(), "add_node", nodes_ur[i].name, nodes_ur[i].node, nodes_ur[i].position);
|
|
|
- }
|
|
|
-
|
|
|
- for (int i = 0; i < transitions_ur.size(); i++) {
|
|
|
- undo_redo->add_do_method(group_sm.ptr(), "add_transition", transitions_ur[i].new_from, transitions_ur[i].new_to, transitions_ur[i].transition);
|
|
|
- undo_redo->add_undo_method(state_machine.ptr(), "add_transition", transitions_ur[i].old_from, transitions_ur[i].old_to, transitions_ur[i].transition);
|
|
|
- }
|
|
|
-
|
|
|
- undo_redo->add_do_method(this, "_update_graph");
|
|
|
- undo_redo->add_undo_method(this, "_update_graph");
|
|
|
- undo_redo->commit_action();
|
|
|
- updating = false;
|
|
|
-
|
|
|
- selected_nodes.clear();
|
|
|
- selected_nodes.insert(group_name);
|
|
|
- state_machine_draw->queue_redraw();
|
|
|
- accept_event();
|
|
|
- _update_mode();
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-void AnimationNodeStateMachineEditor::_ungroup_selected_nodes() {
|
|
|
- bool find = false;
|
|
|
- HashSet<StringName> new_selected_nodes;
|
|
|
-
|
|
|
- for (const StringName &E : selected_nodes) {
|
|
|
- Ref<AnimationNodeStateMachine> group_sm = state_machine->get_node(E);
|
|
|
-
|
|
|
- if (group_sm.is_valid()) {
|
|
|
- find = true;
|
|
|
-
|
|
|
- Vector2 group_position = state_machine->get_node_position(E);
|
|
|
- StringName group_name = E;
|
|
|
-
|
|
|
- List<AnimationNode::ChildNode> nodes;
|
|
|
- group_sm->get_child_nodes(&nodes);
|
|
|
-
|
|
|
- Vector<NodeUR> nodes_ur;
|
|
|
- Vector<TransitionUR> transitions_ur;
|
|
|
-
|
|
|
- updating = true;
|
|
|
- EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
|
|
|
- undo_redo->create_action("Ungroup");
|
|
|
-
|
|
|
- // Move all child nodes to current state machine
|
|
|
- for (int i = 0; i < nodes.size(); i++) {
|
|
|
- if (!group_sm->can_edit_node(nodes[i].name)) {
|
|
|
- continue;
|
|
|
- }
|
|
|
-
|
|
|
- Vector2 node_position = group_sm->get_node_position(nodes[i].name);
|
|
|
-
|
|
|
- NodeUR new_node;
|
|
|
- new_node.name = nodes[i].name;
|
|
|
- new_node.position = node_position;
|
|
|
- new_node.node = nodes[i].node;
|
|
|
-
|
|
|
- nodes_ur.push_back(new_node);
|
|
|
- }
|
|
|
-
|
|
|
- for (int i = 0; i < group_sm->get_transition_count(); i++) {
|
|
|
- String from = group_sm->get_transition_from(i);
|
|
|
- String to = group_sm->get_transition_to(i);
|
|
|
- Ref<AnimationNodeStateMachineTransition> tr = group_sm->get_transition(i);
|
|
|
-
|
|
|
- TransitionUR new_tr;
|
|
|
- new_tr.new_from = from.replace_first("../", "");
|
|
|
- new_tr.new_to = to.replace_first("../", "");
|
|
|
- new_tr.old_from = from;
|
|
|
- new_tr.old_to = to;
|
|
|
- new_tr.transition = tr;
|
|
|
-
|
|
|
- transitions_ur.push_back(new_tr);
|
|
|
- }
|
|
|
-
|
|
|
- for (int i = 0; i < nodes_ur.size(); i++) {
|
|
|
- undo_redo->add_do_method(group_sm.ptr(), "remove_node", nodes_ur[i].name);
|
|
|
- undo_redo->add_undo_method(state_machine.ptr(), "remove_node", nodes_ur[i].name);
|
|
|
- }
|
|
|
-
|
|
|
- undo_redo->add_do_method(state_machine.ptr(), "remove_node", group_name);
|
|
|
- undo_redo->add_undo_method(state_machine.ptr(), "add_node", group_name, group_sm, group_position);
|
|
|
-
|
|
|
- for (int i = 0; i < nodes_ur.size(); i++) {
|
|
|
- new_selected_nodes.insert(nodes_ur[i].name);
|
|
|
- undo_redo->add_do_method(state_machine.ptr(), "add_node", nodes_ur[i].name, nodes_ur[i].node, nodes_ur[i].position);
|
|
|
- undo_redo->add_undo_method(group_sm.ptr(), "add_node", nodes_ur[i].name, nodes_ur[i].node, nodes_ur[i].position);
|
|
|
- }
|
|
|
-
|
|
|
- for (int i = 0; i < transitions_ur.size(); i++) {
|
|
|
- if (transitions_ur[i].old_from != state_machine->start_node && transitions_ur[i].old_to != state_machine->end_node) {
|
|
|
- undo_redo->add_do_method(state_machine.ptr(), "add_transition", transitions_ur[i].new_from, transitions_ur[i].new_to, transitions_ur[i].transition);
|
|
|
- }
|
|
|
-
|
|
|
- undo_redo->add_undo_method(group_sm.ptr(), "add_transition", transitions_ur[i].old_from, transitions_ur[i].old_to, transitions_ur[i].transition);
|
|
|
- }
|
|
|
-
|
|
|
- for (int i = 0; i < state_machine->get_transition_count(); i++) {
|
|
|
- String from = state_machine->get_transition_from(i);
|
|
|
- String to = state_machine->get_transition_to(i);
|
|
|
- Ref<AnimationNodeStateMachineTransition> tr = state_machine->get_transition(i);
|
|
|
-
|
|
|
- if (from == group_name || to == group_name) {
|
|
|
- undo_redo->add_undo_method(state_machine.ptr(), "add_transition", from, to, tr);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- undo_redo->add_do_method(this, "_update_graph");
|
|
|
- undo_redo->add_undo_method(this, "_update_graph");
|
|
|
- undo_redo->commit_action();
|
|
|
- updating = false;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- if (find) {
|
|
|
- selected_nodes = new_selected_nodes;
|
|
|
- selected_node = StringName();
|
|
|
- state_machine_draw->queue_redraw();
|
|
|
- accept_event();
|
|
|
- _update_mode();
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
void AnimationNodeStateMachineEditor::_open_menu(const Vector2 &p_position) {
|
|
|
AnimationTree *tree = AnimationTreeEditor::get_singleton()->get_animation_tree();
|
|
|
if (!tree) {
|
|
@@ -790,83 +582,8 @@ void AnimationNodeStateMachineEditor::_open_menu(const Vector2 &p_position) {
|
|
|
add_node_pos = p_position / EDSCALE + state_machine->get_graph_offset();
|
|
|
}
|
|
|
|
|
|
-void AnimationNodeStateMachineEditor::_open_connect_menu(const Vector2 &p_position) {
|
|
|
- ERR_FAIL_COND(connecting_to_node == StringName());
|
|
|
-
|
|
|
- Ref<AnimationNode> node = state_machine->get_node(connecting_to_node);
|
|
|
- Ref<AnimationNodeStateMachine> anodesm = node;
|
|
|
- Ref<AnimationNodeEndState> end_node = node;
|
|
|
- ERR_FAIL_COND(!anodesm.is_valid() && !end_node.is_valid());
|
|
|
-
|
|
|
- connect_menu->clear();
|
|
|
- state_machine_menu->clear();
|
|
|
- end_menu->clear();
|
|
|
- nodes_to_connect.clear();
|
|
|
-
|
|
|
- for (int i = connect_menu->get_child_count() - 1; i >= 0; i--) {
|
|
|
- Node *child = connect_menu->get_child(i);
|
|
|
-
|
|
|
- if (child->is_class("PopupMenu")) {
|
|
|
- connect_menu->remove_child(child);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- connect_menu->reset_size();
|
|
|
- state_machine_menu->reset_size();
|
|
|
- end_menu->reset_size();
|
|
|
-
|
|
|
- if (anodesm.is_valid()) {
|
|
|
- _create_submenu(connect_menu, anodesm, connecting_to_node, connecting_to_node);
|
|
|
- } else {
|
|
|
- _create_submenu(connect_menu, state_machine, connecting_to_node, connecting_to_node, true);
|
|
|
- }
|
|
|
-
|
|
|
- connect_menu->add_submenu_item(TTR("To") + " Animation", connecting_to_node);
|
|
|
-
|
|
|
- if (state_machine_menu->get_item_count() > 0 || !end_node.is_valid()) {
|
|
|
- connect_menu->add_submenu_item(TTR("To") + " StateMachine", "state_machines");
|
|
|
- connect_menu->add_child(state_machine_menu);
|
|
|
- }
|
|
|
-
|
|
|
- if (end_node.is_valid()) {
|
|
|
- connect_menu->add_submenu_item(TTR("To") + " End", "end_nodes");
|
|
|
- connect_menu->add_child(end_menu);
|
|
|
- } else {
|
|
|
- state_machine_menu->add_item(connecting_to_node, nodes_to_connect.size());
|
|
|
- }
|
|
|
-
|
|
|
- nodes_to_connect.push_back(connecting_to_node);
|
|
|
-
|
|
|
- if (nodes_to_connect.size() == 1) {
|
|
|
- _add_transition();
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- connect_menu->set_position(state_machine_draw->get_screen_transform().xform(p_position));
|
|
|
- connect_menu->popup();
|
|
|
-}
|
|
|
-
|
|
|
-bool AnimationNodeStateMachineEditor::_create_submenu(PopupMenu *p_menu, Ref<AnimationNodeStateMachine> p_nodesm, const StringName &p_name, const StringName &p_path, bool from_root, Vector<Ref<AnimationNodeStateMachine>> p_parents) {
|
|
|
+bool AnimationNodeStateMachineEditor::_create_submenu(PopupMenu *p_menu, Ref<AnimationNodeStateMachine> p_nodesm, const StringName &p_name, const StringName &p_path) {
|
|
|
String prev_path;
|
|
|
- Vector<Ref<AnimationNodeStateMachine>> parents = p_parents;
|
|
|
-
|
|
|
- if (from_root && p_nodesm->get_prev_state_machine() == nullptr) {
|
|
|
- return false;
|
|
|
- }
|
|
|
-
|
|
|
- if (from_root) {
|
|
|
- AnimationNodeStateMachine *prev = p_nodesm->get_prev_state_machine();
|
|
|
-
|
|
|
- while (prev != nullptr) {
|
|
|
- parents.push_back(prev);
|
|
|
- p_nodesm = Ref<AnimationNodeStateMachine>(prev);
|
|
|
- prev_path += "../";
|
|
|
- prev = prev->get_prev_state_machine();
|
|
|
- }
|
|
|
- end_menu->add_item("Root", nodes_to_connect.size());
|
|
|
- nodes_to_connect.push_back(prev_path + state_machine->end_node);
|
|
|
- prev_path.remove_at(prev_path.size() - 1);
|
|
|
- }
|
|
|
|
|
|
List<StringName> nodes;
|
|
|
p_nodesm->get_node_list(&nodes);
|
|
@@ -881,12 +598,7 @@ bool AnimationNodeStateMachineEditor::_create_submenu(PopupMenu *p_menu, Ref<Ani
|
|
|
if (p_nodesm->can_edit_node(E)) {
|
|
|
Ref<AnimationNodeStateMachine> ansm = p_nodesm->get_node(E);
|
|
|
|
|
|
- String path;
|
|
|
- if (from_root) {
|
|
|
- path = prev_path + "/" + E;
|
|
|
- } else {
|
|
|
- path = String(p_path) + "/" + E;
|
|
|
- }
|
|
|
+ String path = String(p_path) + "/" + E;
|
|
|
|
|
|
if (ansm == state_machine) {
|
|
|
end_menu->add_item(E, nodes_to_connect.size());
|
|
@@ -895,25 +607,10 @@ bool AnimationNodeStateMachineEditor::_create_submenu(PopupMenu *p_menu, Ref<Ani
|
|
|
}
|
|
|
|
|
|
if (ansm.is_valid()) {
|
|
|
- bool parent_found = false;
|
|
|
-
|
|
|
- for (int i = 0; i < parents.size(); i++) {
|
|
|
- if (parents[i] == ansm) {
|
|
|
- path = path.replace_first("/../" + E, "");
|
|
|
- parent_found = true;
|
|
|
- break;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- if (parent_found) {
|
|
|
- end_menu->add_item(E, nodes_to_connect.size());
|
|
|
- nodes_to_connect.push_back(path + "/" + state_machine->end_node);
|
|
|
- } else {
|
|
|
- state_machine_menu->add_item(E, nodes_to_connect.size());
|
|
|
- nodes_to_connect.push_back(path);
|
|
|
- }
|
|
|
+ state_machine_menu->add_item(E, nodes_to_connect.size());
|
|
|
+ nodes_to_connect.push_back(path);
|
|
|
|
|
|
- if (_create_submenu(nodes_menu, ansm, E, path, false, parents)) {
|
|
|
+ if (_create_submenu(nodes_menu, ansm, E, path)) {
|
|
|
nodes_menu->add_submenu_item(E, E);
|
|
|
node_added = true;
|
|
|
}
|
|
@@ -939,7 +636,6 @@ void AnimationNodeStateMachineEditor::_delete_selected() {
|
|
|
while (item) {
|
|
|
if (!updating) {
|
|
|
updating = true;
|
|
|
- selected_multi_transition = TransitionLine();
|
|
|
undo_redo->create_action("Transition(s) Removed");
|
|
|
}
|
|
|
|
|
@@ -959,18 +655,10 @@ void AnimationNodeStateMachineEditor::_delete_selected() {
|
|
|
}
|
|
|
|
|
|
void AnimationNodeStateMachineEditor::_delete_all() {
|
|
|
- Vector<TransitionLine> multi_transitions = selected_multi_transition.multi_transitions;
|
|
|
- selected_multi_transition = TransitionLine();
|
|
|
-
|
|
|
updating = true;
|
|
|
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
|
|
|
undo_redo->create_action("Transition(s) Removed");
|
|
|
_erase_selected(true);
|
|
|
- for (int i = 0; i < multi_transitions.size(); i++) {
|
|
|
- selected_transition_from = multi_transitions[i].from_node;
|
|
|
- selected_transition_to = multi_transitions[i].to_node;
|
|
|
- _erase_selected(true);
|
|
|
- }
|
|
|
undo_redo->commit_action();
|
|
|
updating = false;
|
|
|
|
|
@@ -1120,14 +808,19 @@ void AnimationNodeStateMachineEditor::_add_transition(const bool p_nested_action
|
|
|
selected_transition_to = connecting_to_node;
|
|
|
selected_transition_index = transition_lines.size();
|
|
|
|
|
|
- EditorNode::get_singleton()->push_item(tr.ptr(), "", true);
|
|
|
+ if (!state_machine->is_transition_across_group(selected_transition_index)) {
|
|
|
+ EditorNode::get_singleton()->push_item(tr.ptr(), "", true);
|
|
|
+ } else {
|
|
|
+ EditorNode::get_singleton()->push_item(tr.ptr(), "", true);
|
|
|
+ EditorNode::get_singleton()->push_item(nullptr, "", true);
|
|
|
+ }
|
|
|
_update_mode();
|
|
|
}
|
|
|
|
|
|
connecting = false;
|
|
|
}
|
|
|
|
|
|
-void AnimationNodeStateMachineEditor::_connection_draw(const Vector2 &p_from, const Vector2 &p_to, AnimationNodeStateMachineTransition::SwitchMode p_mode, bool p_enabled, bool p_selected, bool p_travel, float p_fade_ratio, bool p_auto_advance, bool p_multi_transitions) {
|
|
|
+void AnimationNodeStateMachineEditor::_connection_draw(const Vector2 &p_from, const Vector2 &p_to, AnimationNodeStateMachineTransition::SwitchMode p_mode, bool p_enabled, bool p_selected, bool p_travel, float p_fade_ratio, bool p_auto_advance, bool p_is_across_group) {
|
|
|
Color linecolor = get_theme_color(SNAME("font_color"), SNAME("Label"));
|
|
|
Color icon_color(1, 1, 1);
|
|
|
Color accent = get_theme_color(SNAME("accent_color"), SNAME("Editor"));
|
|
@@ -1173,11 +866,7 @@ void AnimationNodeStateMachineEditor::_connection_draw(const Vector2 &p_from, co
|
|
|
xf.columns[2] = (p_from + p_to) * 0.5 - xf.columns[1] * icon->get_height() * 0.5 - xf.columns[0] * icon->get_height() * 0.5;
|
|
|
|
|
|
state_machine_draw->draw_set_transform_matrix(xf);
|
|
|
- if (p_multi_transitions) {
|
|
|
- state_machine_draw->draw_texture(icons[0], Vector2(-icon->get_width(), 0), icon_color);
|
|
|
- state_machine_draw->draw_texture(icons[0], Vector2(), icon_color);
|
|
|
- state_machine_draw->draw_texture(icons[0], Vector2(icon->get_width(), 0), icon_color);
|
|
|
- } else {
|
|
|
+ if (!p_is_across_group) {
|
|
|
state_machine_draw->draw_texture(icon, Vector2(), icon_color);
|
|
|
}
|
|
|
state_machine_draw->draw_set_transform_matrix(Transform2D());
|
|
@@ -1344,17 +1033,14 @@ void AnimationNodeStateMachineEditor::_state_machine_draw() {
|
|
|
for (int i = 0; i < state_machine->get_transition_count(); i++) {
|
|
|
TransitionLine tl;
|
|
|
tl.transition_index = i;
|
|
|
+
|
|
|
tl.from_node = state_machine->get_transition_from(i);
|
|
|
- StringName local_from = String(tl.from_node).get_slicec('/', 0);
|
|
|
- local_from = local_from == ".." ? state_machine->start_node : local_from;
|
|
|
- Vector2 ofs_from = (dragging_selected && selected_nodes.has(local_from)) ? drag_ofs : Vector2();
|
|
|
- tl.from = (state_machine->get_node_position(local_from) * EDSCALE) + ofs_from - state_machine->get_graph_offset() * EDSCALE;
|
|
|
+ Vector2 ofs_from = (dragging_selected && selected_nodes.has(tl.from_node)) ? drag_ofs : Vector2();
|
|
|
+ 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);
|
|
|
- StringName local_to = String(tl.to_node).get_slicec('/', 0);
|
|
|
- local_to = local_to == ".." ? state_machine->end_node : local_to;
|
|
|
- Vector2 ofs_to = (dragging_selected && selected_nodes.has(local_to)) ? drag_ofs : Vector2();
|
|
|
- tl.to = (state_machine->get_node_position(local_to) * EDSCALE) + ofs_to - state_machine->get_graph_offset() * EDSCALE;
|
|
|
+ Vector2 ofs_to = (dragging_selected && selected_nodes.has(tl.to_node)) ? drag_ofs : Vector2();
|
|
|
+ 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 = bool(tr->get_advance_mode() == AnimationNodeStateMachineTransition::ADVANCE_MODE_DISABLED);
|
|
@@ -1366,35 +1052,36 @@ void AnimationNodeStateMachineEditor::_state_machine_draw() {
|
|
|
tl.travel = false;
|
|
|
tl.fade_ratio = 0.0;
|
|
|
tl.hidden = false;
|
|
|
+ tl.is_across_group = state_machine->is_transition_across_group(i);
|
|
|
|
|
|
- if (state_machine->has_local_transition(local_to, local_from)) { //offset if same exists
|
|
|
+ if (state_machine->has_transition(tl.to_node, tl.from_node)) { //offset if same exists
|
|
|
Vector2 offset = -(tl.from - tl.to).normalized().orthogonal() * tr_bidi_offset;
|
|
|
tl.from += offset;
|
|
|
tl.to += offset;
|
|
|
}
|
|
|
|
|
|
for (int j = 0; j < node_rects.size(); j++) {
|
|
|
- if (node_rects[j].node_name == local_from) {
|
|
|
+ if (node_rects[j].node_name == tl.from_node) {
|
|
|
_clip_src_line_to_rect(tl.from, tl.to, node_rects[j].node);
|
|
|
}
|
|
|
- if (node_rects[j].node_name == local_to) {
|
|
|
+ if (node_rects[j].node_name == tl.to_node) {
|
|
|
_clip_dst_line_to_rect(tl.from, tl.to, node_rects[j].node);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
tl.selected = selected_transition_from == tl.from_node && selected_transition_to == tl.to_node;
|
|
|
|
|
|
- if (blend_from == local_from && current == local_to) {
|
|
|
+ if (blend_from == tl.from_node && current == tl.to_node) {
|
|
|
tl.travel = true;
|
|
|
tl.fade_ratio = MIN(1.0, fading_pos / fading_time);
|
|
|
}
|
|
|
|
|
|
if (travel_path.size()) {
|
|
|
- if (current == local_from && travel_path[0] == local_to) {
|
|
|
+ if (current == tl.from_node && travel_path[0] == tl.to_node) {
|
|
|
tl.travel = true;
|
|
|
} else {
|
|
|
for (int j = 0; j < travel_path.size() - 1; j++) {
|
|
|
- if (travel_path[j] == local_from && travel_path[j + 1] == local_to) {
|
|
|
+ if (travel_path[j] == tl.from_node && travel_path[j + 1] == tl.to_node) {
|
|
|
tl.travel = true;
|
|
|
break;
|
|
|
}
|
|
@@ -1408,17 +1095,11 @@ void AnimationNodeStateMachineEditor::_state_machine_draw() {
|
|
|
tl.auto_advance = true;
|
|
|
}
|
|
|
|
|
|
- // check if already have this local transition
|
|
|
+ // check if already have this transition
|
|
|
for (int j = 0; j < transition_lines.size(); j++) {
|
|
|
- StringName from = String(transition_lines[j].from_node).get_slicec('/', 0);
|
|
|
- StringName to = String(transition_lines[j].to_node).get_slicec('/', 0);
|
|
|
- from = from == ".." ? state_machine->start_node : from;
|
|
|
- to = to == ".." ? state_machine->end_node : to;
|
|
|
-
|
|
|
- if (from == local_from && to == local_to) {
|
|
|
+ if (transition_lines[j].from_node == tl.from_node && transition_lines[j].to_node == tl.to_node) {
|
|
|
tl.hidden = true;
|
|
|
transition_lines.write[j].disabled = transition_lines[j].disabled && tl.disabled;
|
|
|
- transition_lines.write[j].multi_transitions.push_back(tl);
|
|
|
}
|
|
|
}
|
|
|
transition_lines.push_back(tl);
|
|
@@ -1427,7 +1108,7 @@ void AnimationNodeStateMachineEditor::_state_machine_draw() {
|
|
|
for (int i = 0; i < transition_lines.size(); i++) {
|
|
|
TransitionLine tl = transition_lines[i];
|
|
|
if (!tl.hidden) {
|
|
|
- _connection_draw(tl.from, tl.to, tl.mode, !tl.disabled, tl.selected, tl.travel, tl.fade_ratio, tl.auto_advance, !tl.multi_transitions.is_empty());
|
|
|
+ _connection_draw(tl.from, tl.to, tl.mode, !tl.disabled, tl.selected, tl.travel, tl.fade_ratio, tl.auto_advance, tl.is_across_group);
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -1481,6 +1162,7 @@ void AnimationNodeStateMachineEditor::_state_machine_draw() {
|
|
|
state_machine_draw->draw_string(font, nr.name.position + Vector2(0, font->get_ascent(font_size)), name, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, font_color);
|
|
|
offset.x += strsize + sep;
|
|
|
|
|
|
+ nr.can_edit = needs_editor;
|
|
|
if (needs_editor) {
|
|
|
nr.edit.position = offset + Vector2(0, (h - edit->get_height()) / 2).floor();
|
|
|
nr.edit.size = edit->get_size();
|
|
@@ -1546,6 +1228,10 @@ void AnimationNodeStateMachineEditor::_state_machine_pos_draw_individual(String
|
|
|
|
|
|
const NodeRect &nr = node_rects[idx];
|
|
|
|
|
|
+ if (nr.can_edit) {
|
|
|
+ return; // It is not AnimationNodeAnimation.
|
|
|
+ }
|
|
|
+
|
|
|
Vector2 from;
|
|
|
from.x = nr.play.position.x;
|
|
|
from.y = (nr.play.position.y + nr.play.size.y + nr.node.position.y + nr.node.size.y) * 0.5;
|
|
@@ -1584,14 +1270,14 @@ void AnimationNodeStateMachineEditor::_state_machine_pos_draw_all() {
|
|
|
{
|
|
|
float len = MAX(0.0001, current_length);
|
|
|
float pos = CLAMP(current_play_pos, 0, len);
|
|
|
- float c = pos / len;
|
|
|
+ float c = current_length == HUGE_LENGTH ? 1 : (pos / len);
|
|
|
_state_machine_pos_draw_individual(playback->get_current_node(), c);
|
|
|
}
|
|
|
|
|
|
{
|
|
|
float len = MAX(0.0001, fade_from_length);
|
|
|
float pos = CLAMP(fade_from_current_play_pos, 0, len);
|
|
|
- float c = pos / len;
|
|
|
+ float c = fade_from_length == HUGE_LENGTH ? 1 : (pos / len);
|
|
|
_state_machine_pos_draw_individual(playback->get_fading_from_node(), c);
|
|
|
}
|
|
|
}
|
|
@@ -1630,8 +1316,6 @@ void AnimationNodeStateMachineEditor::_notification(int p_what) {
|
|
|
auto_advance->set_icon(get_theme_icon(SNAME("AutoPlay"), SNAME("EditorIcons")));
|
|
|
|
|
|
tool_erase->set_icon(get_theme_icon(SNAME("Remove"), SNAME("EditorIcons")));
|
|
|
- tool_group->set_icon(get_theme_icon(SNAME("Group"), SNAME("EditorIcons")));
|
|
|
- tool_ungroup->set_icon(get_theme_icon(SNAME("Ungroup"), SNAME("EditorIcons")));
|
|
|
|
|
|
play_mode->clear();
|
|
|
play_mode->add_icon_item(get_theme_icon(SNAME("PlayTravel"), SNAME("EditorIcons")), TTR("Travel"));
|
|
@@ -1655,10 +1339,6 @@ void AnimationNodeStateMachineEditor::_notification(int p_what) {
|
|
|
error = TTR("AnimationTree is inactive.\nActivate to enable playback, check node warnings if activation fails.");
|
|
|
} else if (tree->is_state_invalid()) {
|
|
|
error = 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");
|
|
|
}
|
|
@@ -1780,8 +1460,12 @@ void AnimationNodeStateMachineEditor::_notification(int p_what) {
|
|
|
|
|
|
while (anodesm.is_valid()) {
|
|
|
current_node_playback = tree->get(AnimationTreeEditor::get_singleton()->get_base_path() + next + "/playback");
|
|
|
- next += "/" + current_node_playback->get_current_node();
|
|
|
- anodesm = anodesm->get_node(current_node_playback->get_current_node());
|
|
|
+ StringName cnode = current_node_playback->get_current_node();
|
|
|
+ next += "/" + cnode;
|
|
|
+ if (!anodesm->has_node(cnode)) {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ anodesm = anodesm->get_node(cnode);
|
|
|
}
|
|
|
|
|
|
// when current_node is a state machine, use playback of current_node to set play_pos
|
|
@@ -1883,10 +1567,7 @@ void AnimationNodeStateMachineEditor::_erase_selected(const bool p_nested_action
|
|
|
for (int j = 0; j < state_machine->get_transition_count(); j++) {
|
|
|
String from = state_machine->get_transition_from(j);
|
|
|
String to = state_machine->get_transition_to(j);
|
|
|
- String local_from = from.get_slicec('/', 0);
|
|
|
- String local_to = to.get_slicec('/', 0);
|
|
|
-
|
|
|
- if (local_from == node_rects[i].node_name || local_to == node_rects[i].node_name) {
|
|
|
+ if (from == node_rects[i].node_name || to == node_rects[i].node_name) {
|
|
|
undo_redo->add_undo_method(state_machine.ptr(), "add_transition", from, to, state_machine->get_transition(j));
|
|
|
}
|
|
|
}
|
|
@@ -1903,30 +1584,6 @@ void AnimationNodeStateMachineEditor::_erase_selected(const bool p_nested_action
|
|
|
selected_nodes.clear();
|
|
|
}
|
|
|
|
|
|
- if (!selected_multi_transition.multi_transitions.is_empty()) {
|
|
|
- delete_tree->clear();
|
|
|
-
|
|
|
- TreeItem *root = delete_tree->create_item();
|
|
|
-
|
|
|
- TreeItem *item = delete_tree->create_item(root);
|
|
|
- item->set_cell_mode(0, TreeItem::CELL_MODE_CHECK);
|
|
|
- item->set_text(0, String(selected_transition_from) + " -> " + selected_transition_to);
|
|
|
- item->set_editable(0, true);
|
|
|
-
|
|
|
- for (int i = 0; i < selected_multi_transition.multi_transitions.size(); i++) {
|
|
|
- String from = selected_multi_transition.multi_transitions[i].from_node;
|
|
|
- String to = selected_multi_transition.multi_transitions[i].to_node;
|
|
|
-
|
|
|
- item = delete_tree->create_item(root);
|
|
|
- item->set_cell_mode(0, TreeItem::CELL_MODE_CHECK);
|
|
|
- item->set_text(0, from + " -> " + to);
|
|
|
- item->set_editable(0, true);
|
|
|
- }
|
|
|
-
|
|
|
- delete_window->popup_centered(Vector2(400, 200));
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
if (selected_transition_to != StringName() && selected_transition_from != StringName() && state_machine->has_transition(selected_transition_from, selected_transition_to)) {
|
|
|
Ref<AnimationNodeStateMachineTransition> tr = state_machine->get_transition(state_machine->find_transition(selected_transition_from, selected_transition_to));
|
|
|
if (!p_nested_action) {
|
|
@@ -1945,7 +1602,6 @@ void AnimationNodeStateMachineEditor::_erase_selected(const bool p_nested_action
|
|
|
selected_transition_from = StringName();
|
|
|
selected_transition_to = StringName();
|
|
|
selected_transition_index = -1;
|
|
|
- selected_multi_transition = TransitionLine();
|
|
|
}
|
|
|
|
|
|
state_machine_draw->queue_redraw();
|
|
@@ -1957,24 +1613,6 @@ void AnimationNodeStateMachineEditor::_update_mode() {
|
|
|
bool nothing_selected = selected_nodes.is_empty() && selected_transition_from == StringName() && selected_transition_to == StringName();
|
|
|
bool start_end_selected = selected_nodes.size() == 1 && (*selected_nodes.begin() == state_machine->start_node || *selected_nodes.begin() == state_machine->end_node);
|
|
|
tool_erase->set_disabled(nothing_selected || start_end_selected || read_only);
|
|
|
-
|
|
|
- if (selected_nodes.is_empty() || start_end_selected || read_only) {
|
|
|
- tool_group->set_disabled(true);
|
|
|
- tool_group->set_visible(true);
|
|
|
- tool_ungroup->set_visible(false);
|
|
|
- } else {
|
|
|
- Ref<AnimationNodeStateMachine> ansm = state_machine->get_node(*selected_nodes.begin());
|
|
|
-
|
|
|
- if (selected_nodes.size() == 1 && ansm.is_valid()) {
|
|
|
- tool_group->set_disabled(true);
|
|
|
- tool_group->set_visible(false);
|
|
|
- tool_ungroup->set_visible(true);
|
|
|
- } else {
|
|
|
- tool_group->set_disabled(false);
|
|
|
- tool_group->set_visible(true);
|
|
|
- tool_ungroup->set_visible(false);
|
|
|
- }
|
|
|
- }
|
|
|
} else {
|
|
|
selection_tools_hb->hide();
|
|
|
}
|
|
@@ -2037,20 +1675,6 @@ AnimationNodeStateMachineEditor::AnimationNodeStateMachineEditor() {
|
|
|
top_hb->add_child(selection_tools_hb);
|
|
|
selection_tools_hb->add_child(memnew(VSeparator));
|
|
|
|
|
|
- tool_group = memnew(Button);
|
|
|
- tool_group->set_flat(true);
|
|
|
- tool_group->set_tooltip_text(TTR("Group Selected Node(s)") + " (Ctrl+G)");
|
|
|
- tool_group->connect("pressed", callable_mp(this, &AnimationNodeStateMachineEditor::_group_selected_nodes));
|
|
|
- tool_group->set_disabled(true);
|
|
|
- selection_tools_hb->add_child(tool_group);
|
|
|
-
|
|
|
- tool_ungroup = memnew(Button);
|
|
|
- tool_ungroup->set_flat(true);
|
|
|
- tool_ungroup->set_tooltip_text(TTR("Ungroup Selected Node") + " (Ctrl+Shift+G)");
|
|
|
- tool_ungroup->connect("pressed", callable_mp(this, &AnimationNodeStateMachineEditor::_ungroup_selected_nodes));
|
|
|
- tool_ungroup->set_visible(false);
|
|
|
- selection_tools_hb->add_child(tool_ungroup);
|
|
|
-
|
|
|
tool_erase = memnew(Button);
|
|
|
tool_erase->set_flat(true);
|
|
|
tool_erase->set_tooltip_text(TTR("Remove selected node or transition."));
|