diff options
author | K. S. Ernest (iFire) Lee <ernest.lee@chibifire.com> | 2018-05-12 20:34:35 -0700 |
---|---|---|
committer | K. S. Ernest (iFire) Lee <ernest.lee@chibifire.com> | 2018-07-24 13:59:28 -0700 |
commit | 85670726fdf840da22d3ab1fe55de4162e9289df (patch) | |
tree | 8f7a4eb8e0b265c94f7295090a01e4db75c4152d | |
parent | 9377f8db55abffc0532e797a3ff6bb39aaac9228 (diff) |
Improve VisualScript UX
* Prototype faster function call ux.
* Work on general search ux.
* Able to create nodes from search.
* Show class for variables but not methods.
* Get actions search working.
* Descriptions now show for both methods and properties.
* Enable zooming on mouse wheel up and down.
* Make the drag trigger on right mouse button.
* Search now shows for action visual script nodes.
* Able to search visual node names.
* Search works better.
* Change zooming scale to hide artifacts better.
* Remove zoom changes
* Select from base should check properties too like the other functions.
* Seq_connect flag is needed to set sequence lines correctly.
* Remove comment
* Code cleanup with function names and arguments.
* Use brief description for search descriptions.
* Clean and fix bug with input nodes connecting with sequence lines.
* Add a warning and fix some edge conditions with sequence into data lines and vice versa.
* Don't search functions when pulling from a sequence node.
* Don't show actions when pulling from a data line.
* Set set and get properties.
* Convert visual script operators to the correct type
* Create a function preset finds only functions.
* Singletons can now find functions.
* Add shift-a for generic search.
* Add brief descriptions for Visual Script nodes.
* Search boxes can now filter names.
* Add bigger hit zones to node connect.
* For the drop zones, make all the rect2 areas the same size.
* Function names in visual script node should be lower case so that search works better.
* Use the convention of capitalize() for set, set, visual script nodes and methods.
* Make search more general. Ignore "_" and make case-insensitive. Also made the search window smaller and remove extra info from search
* Make type_cast use the connecting node's type and remove use of found variable.
* Fix case where you call an instance's call function where it becomes an invalid call.
* Make get_visual_node_names use a set of filters, move action creations and fix bug with sequence node connections.
* Make the window bigger.
* Make connect_data and connect_seq more robust.
* Add icons to search items.
* Add vs constructors in shift-a menu.
* Operators, builtins and constructors show type name. Fix several problems with port connections.
* In shift-a mode search everything.
* Code cleanup
* Work on autocompleting the type.
* Use type guess in action creation.
* Check if type hint string exists in object variables when generating the visual script search.
* Add the hint to SceneTree.
* Add original type detection.
* Make type casting great again. This puts the type casted base type as the data output type string hint.
* Pass the type in a VisualScriptFunctionCall too.
* Set the base type correctly in VisualScriptFunctionGet and VisualScriptFunctionSet using hint string.
* Make sure the instance is passed in VisualScriptPropertySet.
* Restore search on the node's type.
* Remove dependencies from graph_edit.
* Remove dependencies from property_selector and name the class visual_script_property_selector.
* Extract hot zones into a function.
* Move hot_zones constants into default theme.
* Bigger capture zones.
* Clean messy port_grab_distance variables.
* Remove RMB functionality.
* Remove memory leak on showing visual script descriptions.
* Read the port_grab_distance constants on enter tree and theme changed.
-rw-r--r-- | modules/visual_script/visual_script.cpp | 13 | ||||
-rw-r--r-- | modules/visual_script/visual_script.h | 1 | ||||
-rw-r--r-- | modules/visual_script/visual_script_editor.cpp | 448 | ||||
-rw-r--r-- | modules/visual_script/visual_script_editor.h | 39 | ||||
-rw-r--r-- | modules/visual_script/visual_script_flow_control.cpp | 2 | ||||
-rw-r--r-- | modules/visual_script/visual_script_func_nodes.cpp | 8 | ||||
-rw-r--r-- | modules/visual_script/visual_script_nodes.cpp | 2 | ||||
-rw-r--r-- | modules/visual_script/visual_script_property_selector.cpp | 705 | ||||
-rw-r--r-- | modules/visual_script/visual_script_property_selector.h | 92 | ||||
-rw-r--r-- | scene/gui/graph_edit.cpp | 26 | ||||
-rw-r--r-- | scene/gui/graph_edit.h | 6 | ||||
-rw-r--r-- | scene/resources/default_theme/default_theme.cpp | 4 |
12 files changed, 1168 insertions, 178 deletions
diff --git a/modules/visual_script/visual_script.cpp b/modules/visual_script/visual_script.cpp index af9fea6681..9dea7a9c9e 100644 --- a/modules/visual_script/visual_script.cpp +++ b/modules/visual_script/visual_script.cpp @@ -1333,6 +1333,19 @@ VisualScript::VisualScript() { base_type = "Object"; } +Set<int> VisualScript::get_output_sequence_ports_connected(const String &edited_func, int from_node) { + List<VisualScript::SequenceConnection> *sc = memnew(List<VisualScript::SequenceConnection>); + get_sequence_connection_list(edited_func, sc); + Set<int> connected; + for (List<VisualScript::SequenceConnection>::Element *E = sc->front(); E; E = E->next()) { + if (E->get().from_node == from_node) { + connected.insert(E->get().from_output); + } + } + memdelete(sc); + return connected; +} + VisualScript::~VisualScript() { while (!functions.empty()) { diff --git a/modules/visual_script/visual_script.h b/modules/visual_script/visual_script.h index b163203a3a..2ad72a40c0 100644 --- a/modules/visual_script/visual_script.h +++ b/modules/visual_script/visual_script.h @@ -319,6 +319,7 @@ public: void custom_signal_swap_argument(const StringName &p_func, int p_argidx, int p_with_argidx); void remove_custom_signal(const StringName &p_name); void rename_custom_signal(const StringName &p_name, const StringName &p_new_name); + Set<int> get_output_sequence_ports_connected(const String &edited_func, int from_node); void get_custom_signal_list(List<StringName> *r_custom_signals) const; diff --git a/modules/visual_script/visual_script_editor.cpp b/modules/visual_script/visual_script_editor.cpp index 873cc293c9..043ccaa01d 100644 --- a/modules/visual_script/visual_script_editor.cpp +++ b/modules/visual_script/visual_script_editor.cpp @@ -33,8 +33,10 @@ #include "core/script_language.h" #include "editor/editor_node.h" #include "editor/editor_resource_preview.h" +#include "object.h" #include "os/input.h" #include "os/keyboard.h" +#include "variant.h" #include "visual_script_expression.h" #include "visual_script_flow_control.h" #include "visual_script_func_nodes.h" @@ -1325,6 +1327,12 @@ void VisualScriptEditor::_input(const Ref<InputEvent> &p_event) { if (mb.is_valid() && !mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT) { revert_on_drag = String(); //so we can still drag functions } + + Ref<InputEventKey> k = p_event; + if (k.is_valid() && k->get_scancode() == KEY_A && k->get_shift() && k->is_pressed()) { + new_connect_node_select->select_from_visual_script(String("")); + accept_event(); + } } void VisualScriptEditor::_members_gui_input(const Ref<InputEvent> &p_event) { @@ -1780,7 +1788,7 @@ void VisualScriptEditor::drop_data_fw(const Point2 &p_point, const Variant &p_da call->set_base_type(node->get_class()); n = call; - method_select->select_method_from_instance(node); + method_select->select_from_instance(node); selecting_method_id = base_id; } @@ -1917,7 +1925,7 @@ void VisualScriptEditor::drop_data_fw(const Point2 &p_point, const Variant &p_da } } -void VisualScriptEditor::_selected_method(const String &p_method) { +void VisualScriptEditor::_selected_method(const String &p_method, const String &p_type) { Ref<VisualScriptFunctionCall> vsfc = script->get_node(edited_func, selecting_method_id); if (!vsfc.is_valid()) @@ -2436,33 +2444,19 @@ void VisualScriptEditor::_graph_connect_to_empty(const String &p_from, int p_fro if (!vsn.is_valid()) return; - if (p_from_slot < vsn->get_output_sequence_port_count()) { + port_action_pos = p_release_pos; - port_action_popup->clear(); - port_action_popup->add_item(TTR("Condition"), CREATE_COND); - port_action_popup->add_item(TTR("Sequence"), CREATE_SEQUENCE); - port_action_popup->add_item(TTR("Switch"), CREATE_SWITCH); - port_action_popup->add_item(TTR("Iterator"), CREATE_ITERATOR); - port_action_popup->add_item(TTR("While"), CREATE_WHILE); - port_action_popup->add_item(TTR("Return"), CREATE_RETURN); + if (p_from_slot < vsn->get_output_sequence_port_count()) { port_action_node = p_from.to_int(); port_action_output = p_from_slot; - + _port_action_menu(CREATE_ACTION); } else { - port_action_popup->clear(); - port_action_popup->add_item(TTR("Call"), CREATE_CALL); - port_action_popup->add_item(TTR("Get"), CREATE_GET); - port_action_popup->add_item(TTR("Set"), CREATE_SET); port_action_output = p_from_slot - vsn->get_output_sequence_port_count(); port_action_node = p_from.to_int(); + _port_action_menu(CREATE_CALL_SET_GET); } - - port_action_pos = p_release_pos; - port_action_popup->set_size(Size2(1, 1)); - port_action_popup->set_position(graph->get_global_position() + p_release_pos); - port_action_popup->popup(); } VisualScriptNode::TypeGuess VisualScriptEditor::_guess_output_type(int p_port_action_node, int p_port_action_output, Set<int> &visited_nodes) { @@ -2530,168 +2524,202 @@ void VisualScriptEditor::_port_action_menu(int p_option) { bool seq_connect = false; - Ref<VisualScriptNode> vnode; Set<int> vn; switch (p_option) { - case CREATE_CALL: { - + case CREATE_CALL_SET_GET: { Ref<VisualScriptFunctionCall> n; n.instance(); - vnode = n; VisualScriptNode::TypeGuess tg = _guess_output_type(port_action_node, port_action_output, vn); - if (tg.type == Variant::OBJECT) { - n->set_call_mode(VisualScriptFunctionCall::CALL_MODE_INSTANCE); - - if (tg.gdclass != StringName()) { - n->set_base_type(tg.gdclass); - } else { - n->set_base_type("Object"); - } + if (tg.gdclass != StringName()) { + n->set_base_type(tg.gdclass); + } else { + n->set_base_type("Object"); + } + String type_string = script->get_node(edited_func, port_action_node)->get_output_value_port_info(port_action_output).hint_string; + if (tg.type == Variant::OBJECT) { if (tg.script.is_valid()) { - n->set_base_script(tg.script->get_path()); - new_connect_node_select->select_method_from_script(tg.script); + new_connect_node_select->select_from_script(tg.script, ""); + } else if (type_string != String()) { + new_connect_node_select->select_from_base_type(type_string); } else { - new_connect_node_select->select_method_from_base_type(n->get_base_type()); + new_connect_node_select->select_from_base_type(n->get_base_type()); } - + } else if (tg.type == Variant::NIL) { + new_connect_node_select->select_from_base_type(""); } else { - n->set_call_mode(VisualScriptFunctionCall::CALL_MODE_BASIC_TYPE); - n->set_basic_type(tg.type); - new_connect_node_select->select_method_from_basic_type(tg.type); + new_connect_node_select->select_from_basic_type(tg.type); } - } break; - case CREATE_SET: { - - Ref<VisualScriptPropertySet> n; - n.instance(); - vnode = n; - + case CREATE_ACTION: { + seq_connect = true; VisualScriptNode::TypeGuess tg = _guess_output_type(port_action_node, port_action_output, vn); - + PropertyInfo property_info = script->get_node(edited_func, port_action_node)->get_output_value_port_info(port_action_output); if (tg.type == Variant::OBJECT) { - n->set_call_mode(VisualScriptPropertySet::CALL_MODE_INSTANCE); - - if (tg.gdclass != StringName()) { - n->set_base_type(tg.gdclass); - } else { - n->set_base_type("Object"); - } - - if (tg.script.is_valid()) { - n->set_base_script(tg.script->get_path()); - new_connect_node_select->select_property_from_script(tg.script); + if (property_info.type == Variant::OBJECT && property_info.hint_string != String()) { + new_connect_node_select->select_from_action(property_info.hint_string); } else { - new_connect_node_select->select_property_from_base_type(n->get_base_type()); + new_connect_node_select->select_from_action(""); } - + } else if (tg.type == Variant::NIL) { + new_connect_node_select->select_from_action(""); } else { - n->set_call_mode(VisualScriptPropertySet::CALL_MODE_BASIC_TYPE); - n->set_basic_type(tg.type); - new_connect_node_select->select_property_from_basic_type(tg.type); + new_connect_node_select->select_from_action(Variant::get_type_name(tg.type)); } } break; - case CREATE_GET: { + } +} - Ref<VisualScriptPropertyGet> n; - n.instance(); - vnode = n; +void VisualScriptEditor::new_node(Ref<VisualScriptNode> vnode, Vector2 ofs) { + Set<int> vn; + Ref<VisualScriptNode> vnode_old = script->get_node(edited_func, port_action_node); + int new_id = script->get_available_id(); + undo_redo->create_action(TTR("Add Node")); + undo_redo->add_do_method(script.ptr(), "add_node", edited_func, new_id, vnode, ofs); + undo_redo->add_undo_method(script.ptr(), "remove_node", edited_func, new_id); + undo_redo->add_do_method(this, "_update_graph", new_id); + undo_redo->add_undo_method(this, "_update_graph", new_id); + undo_redo->commit_action(); - VisualScriptNode::TypeGuess tg = _guess_output_type(port_action_node, port_action_output, vn); + port_action_new_node = new_id; +} - if (tg.type == Variant::OBJECT) { - n->set_call_mode(VisualScriptPropertyGet::CALL_MODE_INSTANCE); +void VisualScriptEditor::connect_data(Ref<VisualScriptNode> vnode_old, Ref<VisualScriptNode> vnode, int new_id) { + undo_redo->create_action(TTR("Connect Node Data")); + VisualScriptReturn *vnode_return = Object::cast_to<VisualScriptReturn>(vnode.ptr()); + if (vnode_return != NULL && vnode_old->get_output_value_port_count() > 0) { + vnode_return->set_enable_return_value(true); + } + if (vnode_old->get_output_value_port_count() <= 0) { + undo_redo->commit_action(); + return; + } + if (vnode->get_input_value_port_count() <= 0) { + undo_redo->commit_action(); + return; + } + int port = port_action_output; + int value_count = vnode_old->get_output_value_port_count(); + if (port >= value_count) { + port = 0; + } + int count = vnode_old->get_output_value_port_count() + vnode_old->get_output_sequence_port_count(); + undo_redo->add_do_method(script.ptr(), "data_connect", edited_func, port_action_node, port, new_id, 0); + undo_redo->add_undo_method(script.ptr(), "data_disconnect", edited_func, port_action_node, port, new_id, 0); + undo_redo->commit_action(); +} - if (tg.gdclass != StringName()) { - n->set_base_type(tg.gdclass); - } else { - n->set_base_type("Object"); - } +void VisualScriptEditor::_selected_connect_node(const String &p_text, const String &p_category) { + Vector2 ofs = graph->get_scroll_ofs() + port_action_pos; + if (graph->is_using_snap()) { + int snap = graph->get_snap(); + ofs = ofs.snapped(Vector2(snap, snap)); + } + ofs /= EDSCALE; - if (tg.script.is_valid()) { - n->set_base_script(tg.script->get_path()); - new_connect_node_select->select_property_from_script(tg.script); - } else { - new_connect_node_select->select_property_from_base_type(n->get_base_type()); - } + Set<int> vn; + if (p_category == "visualscript") { + Ref<VisualScriptNode> vnode_new = VisualScriptLanguage::singleton->create_node_from_name(p_text); + Ref<VisualScriptNode> vnode_old = script->get_node(edited_func, port_action_node); + int new_id = script->get_available_id(); + + if (Object::cast_to<VisualScriptOperator>(vnode_new.ptr())) { + Variant::Type type = script->get_node(edited_func, port_action_node)->get_output_value_port_info(port_action_output).type; + Object::cast_to<VisualScriptOperator>(vnode_new.ptr())->set_typed(type); + } + + if (Object::cast_to<VisualScriptTypeCast>(vnode_new.ptr())) { + Variant::Type type = script->get_node(edited_func, port_action_node)->get_output_value_port_info(port_action_output).type; + String hint_name = script->get_node(edited_func, port_action_node)->get_output_value_port_info(port_action_output).hint_string; + + if (type == Variant::OBJECT) { + Object::cast_to<VisualScriptTypeCast>(vnode_new.ptr())->set_base_type(hint_name); + } else if (type == Variant::NIL) { + Object::cast_to<VisualScriptTypeCast>(vnode_new.ptr())->set_base_type(""); } else { - n->set_call_mode(VisualScriptPropertyGet::CALL_MODE_BASIC_TYPE); - n->set_basic_type(tg.type); - new_connect_node_select->select_property_from_basic_type(tg.type); + Object::cast_to<VisualScriptTypeCast>(vnode_new.ptr())->set_base_type(Variant::get_type_name(type)); } + } + undo_redo->create_action(TTR("Add Node")); + undo_redo->add_do_method(script.ptr(), "add_node", edited_func, new_id, vnode_new, ofs); + connect_seq(vnode_old, vnode_new, new_id); + connect_data(vnode_old, vnode_new, new_id); + undo_redo->add_undo_method(script.ptr(), "remove_node", edited_func, new_id); + undo_redo->add_do_method(this, "_update_graph"); + undo_redo->add_undo_method(this, "_update_graph"); + undo_redo->commit_action(); + return; + } - } break; - case CREATE_COND: { + Ref<VisualScriptNode> vnode; + + seq_connect = false; + if (p_category == String("method")) { + + Ref<VisualScriptFunctionCall> n; + n.instance(); + vnode = n; + } else if (p_category == String("set")) { + + Ref<VisualScriptPropertySet> n; + n.instance(); + n->set_property(p_text); + vnode = n; + } else if (p_category == String("get")) { + + Ref<VisualScriptPropertyGet> n; + n.instance(); + n->set_property(p_text); + vnode = n; + } + + if (p_category == String("action")) { + if (p_text == "VisualScriptCondition") { Ref<VisualScriptCondition> n; n.instance(); vnode = n; seq_connect = true; + } + if (p_text == "VisualScriptSwitch") { - } break; - case CREATE_SEQUENCE: { - - Ref<VisualScriptSequence> n; + Ref<VisualScriptSwitch> n; n.instance(); vnode = n; seq_connect = true; + } else if (p_text == "VisualScriptSequence") { - } break; - case CREATE_SWITCH: { - - Ref<VisualScriptSwitch> n; + Ref<VisualScriptSequence> n; n.instance(); vnode = n; seq_connect = true; - - } break; - case CREATE_ITERATOR: { + } else if (p_text == "VisualScriptIterator") { Ref<VisualScriptIterator> n; n.instance(); vnode = n; seq_connect = true; - - } break; - case CREATE_WHILE: { + } else if (p_text == "VisualScriptWhile") { Ref<VisualScriptWhile> n; n.instance(); vnode = n; seq_connect = true; - - } break; - case CREATE_RETURN: { + } else if (p_text == "VisualScriptReturn") { Ref<VisualScriptReturn> n; n.instance(); vnode = n; seq_connect = true; - - } break; + } } - int new_id = script->get_available_id(); - undo_redo->create_action(TTR("Add Node")); - undo_redo->add_do_method(script.ptr(), "add_node", edited_func, new_id, vnode, ofs); - if (seq_connect) { - undo_redo->add_do_method(script.ptr(), "sequence_connect", edited_func, port_action_node, port_action_output, new_id); - } - undo_redo->add_undo_method(script.ptr(), "remove_node", edited_func, new_id); - undo_redo->add_do_method(this, "_update_graph", new_id); - undo_redo->add_undo_method(this, "_update_graph", new_id); - undo_redo->commit_action(); - - port_action_new_node = new_id; -} - -void VisualScriptEditor::_selected_connect_node_method_or_setget(const String &p_text) { + new_node(vnode, ofs); Ref<VisualScriptNode> vsn = script->get_node(edited_func, port_action_new_node); @@ -2699,28 +2727,152 @@ void VisualScriptEditor::_selected_connect_node_method_or_setget(const String &p Ref<VisualScriptFunctionCall> vsfc = vsn; vsfc->set_function(p_text); - script->data_connect(edited_func, port_action_node, port_action_output, port_action_new_node, 0); + + VisualScriptNode::TypeGuess tg = _guess_output_type(port_action_node, port_action_output, vn); + if (tg.type == Variant::OBJECT) { + vsfc->set_call_mode(VisualScriptFunctionCall::CALL_MODE_INSTANCE); + + if (tg.gdclass != StringName()) { + vsfc->set_base_type(tg.gdclass); + + } else { + PropertyHint hint = script->get_node(edited_func, port_action_node)->get_output_value_port_info(port_action_output).hint; + String base_type = script->get_node(edited_func, port_action_node)->get_output_value_port_info(port_action_output).hint_string; + + if (base_type != String() && hint == PROPERTY_HINT_TYPE_STRING) { + vsfc->set_base_type(base_type); + } + if (p_text == "call" || p_text == "call_deferred") { + vsfc->set_function(""); + } + } + if (tg.script.is_valid()) { + vsfc->set_base_script(tg.script->get_path()); + } + } else if (tg.type == Variant::NIL) { + vsfc->set_call_mode(VisualScriptFunctionCall::CALL_MODE_INSTANCE); + vsfc->set_base_type(script->get_instance_base_type()); + } else { + vsfc->set_call_mode(VisualScriptFunctionCall::CALL_MODE_BASIC_TYPE); + vsfc->set_basic_type(tg.type); + } } if (Object::cast_to<VisualScriptPropertySet>(vsn.ptr())) { Ref<VisualScriptPropertySet> vsp = vsn; - vsp->set_property(p_text); - script->data_connect(edited_func, port_action_node, port_action_output, port_action_new_node, 0); + + VisualScriptNode::TypeGuess tg = _guess_output_type(port_action_node, port_action_output, vn); + if (tg.type == Variant::OBJECT) { + vsp->set_call_mode(VisualScriptPropertySet::CALL_MODE_INSTANCE); + + if (tg.gdclass != StringName()) { + vsp->set_base_type(tg.gdclass); + + } else { + PropertyHint hint = script->get_node(edited_func, port_action_node)->get_output_value_port_info(port_action_output).hint; + String base_type = script->get_node(edited_func, port_action_node)->get_output_value_port_info(port_action_output).hint_string; + + if (base_type != String() && hint == PROPERTY_HINT_TYPE_STRING) { + vsp->set_base_type(base_type); + } + } + if (tg.script.is_valid()) { + vsp->set_base_script(tg.script->get_path()); + } + } else if (tg.type == Variant::NIL) { + vsp->set_call_mode(VisualScriptPropertySet::CALL_MODE_INSTANCE); + vsp->set_base_type(script->get_instance_base_type()); + } else { + vsp->set_call_mode(VisualScriptPropertySet::CALL_MODE_BASIC_TYPE); + vsp->set_basic_type(tg.type); + } } if (Object::cast_to<VisualScriptPropertyGet>(vsn.ptr())) { - Ref<VisualScriptPropertyGet> vsp = vsn; - vsp->set_property(p_text); - script->data_connect(edited_func, port_action_node, port_action_output, port_action_new_node, 0); + + VisualScriptNode::TypeGuess tg = _guess_output_type(port_action_node, port_action_output, vn); + if (tg.type == Variant::OBJECT) { + vsp->set_call_mode(VisualScriptPropertyGet::CALL_MODE_INSTANCE); + + if (tg.gdclass != StringName()) { + vsp->set_base_type(tg.gdclass); + + } else { + PropertyHint hint = script->get_node(edited_func, port_action_node)->get_output_value_port_info(port_action_output).hint; + String base_type = script->get_node(edited_func, port_action_node)->get_output_value_port_info(port_action_output).hint_string; + + if (base_type != String() && hint == PROPERTY_HINT_TYPE_STRING) { + vsp->set_base_type(base_type); + } + } + if (tg.script.is_valid()) { + vsp->set_base_script(tg.script->get_path()); + } + } else if (tg.type == Variant::NIL) { + vsp->set_call_mode(VisualScriptPropertyGet::CALL_MODE_INSTANCE); + vsp->set_base_type(script->get_instance_base_type()); + } else { + vsp->set_call_mode(VisualScriptPropertyGet::CALL_MODE_BASIC_TYPE); + vsp->set_basic_type(tg.type); + } } + Ref<VisualScriptNode> vnode_old = script->get_node(edited_func, port_action_node); + connect_seq(vnode_old, vnode, port_action_new_node); + connect_data(vnode_old, vnode, port_action_new_node); _update_graph(port_action_new_node); _update_graph_connections(); } -void VisualScriptEditor::_selected_new_virtual_method(const String &p_text) { +void VisualScriptEditor::connect_seq(Ref<VisualScriptNode> vnode_old, Ref<VisualScriptNode> vnode_new, int new_id) { + int seq_count = vnode_old->get_output_sequence_port_count(); + VisualScriptOperator *vnode_operator = Object::cast_to<VisualScriptOperator>(vnode_new.ptr()); + if (vnode_operator != NULL && vnode_operator->has_input_sequence_port() == false) { + return; + } + VisualScriptConstructor *vnode_constructor = Object::cast_to<VisualScriptConstructor>(vnode_new.ptr()); + if (vnode_constructor != NULL) { + return; + } + if (vnode_old->get_output_sequence_port_count() <= 0) { + return; + } + if (vnode_new->has_input_sequence_port() == false) { + return; + } + VisualScriptFunction *vnode_function = Object::cast_to<VisualScriptFunction>(vnode_old.ptr()); + undo_redo->create_action(TTR("Connect Node Sequence")); + int pass_port = -vnode_old->get_output_sequence_port_count() + 1; + int return_port = port_action_output - 1; + if (vnode_old->get_output_value_port_info(port_action_output).name == String("pass") && + !script->get_output_sequence_ports_connected(edited_func, port_action_node).has(pass_port)) { + undo_redo->add_do_method(script.ptr(), "sequence_connect", edited_func, port_action_node, pass_port, new_id); + undo_redo->add_undo_method(script.ptr(), "sequence_disconnect", edited_func, port_action_node, pass_port, new_id); + } else if (vnode_old->get_output_value_port_info(port_action_output).name == String("return") && + !script->get_output_sequence_ports_connected(edited_func, port_action_node).has(return_port)) { + undo_redo->add_do_method(script.ptr(), "sequence_connect", edited_func, port_action_node, return_port, new_id); + undo_redo->add_undo_method(script.ptr(), "sequence_disconnect", edited_func, port_action_node, return_port, new_id); + } else { + for (int port = 0; port < vnode_old->get_output_sequence_port_count(); port++) { + int count = vnode_old->get_output_sequence_port_count(); + if (port_action_output < count && !script->get_output_sequence_ports_connected(edited_func, port_action_node).has(port_action_output)) { + undo_redo->add_do_method(script.ptr(), "sequence_connect", edited_func, port_action_node, port_action_output, new_id); + undo_redo->add_undo_method(script.ptr(), "sequence_disconnect", edited_func, port_action_node, port_action_output, new_id); + break; + } else if (!script->get_output_sequence_ports_connected(edited_func, port_action_node).has(port)) { + undo_redo->add_do_method(script.ptr(), "sequence_connect", edited_func, port_action_node, port, new_id); + undo_redo->add_undo_method(script.ptr(), "sequence_disconnect", edited_func, port_action_node, port, new_id); + break; + } + } + } + + undo_redo->commit_action(); +} + +void VisualScriptEditor::_selected_new_virtual_method(const String &p_text, const String &p_category) { String name = p_text; if (script->has_function(name)) { @@ -2777,12 +2929,29 @@ void VisualScriptEditor::_selected_new_virtual_method(const String &p_text) { _update_graph(); } -void VisualScriptEditor::_cancel_connect_node_method_or_setget() { - - script->remove_node(edited_func, port_action_new_node); +void VisualScriptEditor::_cancel_connect_node() { + // Causes crashes + //script->remove_node(edited_func, port_action_new_node); _update_graph(); } +void VisualScriptEditor::_create_new_node(const String &p_text, const String &p_category, const Vector2 &p_point) { + Vector2 ofs = graph->get_scroll_ofs() + p_point; + if (graph->is_using_snap()) { + int snap = graph->get_snap(); + ofs = ofs.snapped(Vector2(snap, snap)); + } + ofs /= EDSCALE; + Ref<VisualScriptNode> vnode = VisualScriptLanguage::singleton->create_node_from_name(p_text); + int new_id = script->get_available_id(); + undo_redo->create_action(TTR("Add Node")); + undo_redo->add_do_method(script.ptr(), "add_node", edited_func, new_id, vnode, ofs); + undo_redo->add_undo_method(script.ptr(), "remove_node", edited_func, new_id); + undo_redo->add_do_method(this, "_update_graph"); + undo_redo->add_undo_method(this, "_update_graph"); + undo_redo->commit_action(); +} + void VisualScriptEditor::_default_value_changed() { Ref<VisualScriptNode> vsn = script->get_node(edited_func, editing_id); @@ -3286,10 +3455,11 @@ void VisualScriptEditor::_bind_methods() { ClassDB::bind_method("_comment_node_resized", &VisualScriptEditor::_comment_node_resized); ClassDB::bind_method("_button_resource_previewed", &VisualScriptEditor::_button_resource_previewed); ClassDB::bind_method("_port_action_menu", &VisualScriptEditor::_port_action_menu); - ClassDB::bind_method("_selected_connect_node_method_or_setget", &VisualScriptEditor::_selected_connect_node_method_or_setget); + ClassDB::bind_method("_selected_connect_node", &VisualScriptEditor::_selected_connect_node); ClassDB::bind_method("_selected_new_virtual_method", &VisualScriptEditor::_selected_new_virtual_method); - ClassDB::bind_method("_cancel_connect_node_method_or_setget", &VisualScriptEditor::_cancel_connect_node_method_or_setget); + ClassDB::bind_method("_cancel_connect_node", &VisualScriptEditor::_cancel_connect_node); + ClassDB::bind_method("_create_new_node", &VisualScriptEditor::_create_new_node); ClassDB::bind_method("_expression_text_changed", &VisualScriptEditor::_expression_text_changed); ClassDB::bind_method("get_drag_data_fw", &VisualScriptEditor::get_drag_data_fw); @@ -3480,25 +3650,21 @@ VisualScriptEditor::VisualScriptEditor() { add_child(default_value_edit); default_value_edit->connect("variant_changed", this, "_default_value_changed"); - method_select = memnew(PropertySelector); + method_select = memnew(VisualScriptPropertySelector); add_child(method_select); method_select->connect("selected", this, "_selected_method"); error_line = -1; - new_connect_node_select = memnew(PropertySelector); + new_connect_node_select = memnew(VisualScriptPropertySelector); add_child(new_connect_node_select); - new_connect_node_select->connect("selected", this, "_selected_connect_node_method_or_setget"); - new_connect_node_select->get_cancel()->connect("pressed", this, "_cancel_connect_node_method_or_setget"); + new_connect_node_select->connect("selected", this, "_selected_connect_node"); + new_connect_node_select->get_cancel()->connect("pressed", this, "_cancel_connect_node"); - new_virtual_method_select = memnew(PropertySelector); + new_virtual_method_select = memnew(VisualScriptPropertySelector); add_child(new_virtual_method_select); new_virtual_method_select->connect("selected", this, "_selected_new_virtual_method"); new_virtual_method_select->get_cancel()->connect("pressed", this, "_selected_new_virtual_method"); - port_action_popup = memnew(PopupMenu); - add_child(port_action_popup); - port_action_popup->connect("id_pressed", this, "_port_action_menu"); - member_popup = memnew(PopupMenu); add_child(member_popup); members->connect("item_rmb_selected", this, "_member_rmb_selected"); diff --git a/modules/visual_script/visual_script_editor.h b/modules/visual_script/visual_script_editor.h index 0bd64d6a1d..b0a27d971a 100644 --- a/modules/visual_script/visual_script_editor.h +++ b/modules/visual_script/visual_script_editor.h @@ -34,9 +34,9 @@ #include "editor/create_dialog.h" #include "editor/plugins/script_editor_plugin.h" #include "editor/property_editor.h" -#include "editor/property_selector.h" #include "scene/gui/graph_edit.h" #include "visual_script.h" +#include "visual_script_property_selector.h" class VisualScriptEditorSignalEdit; class VisualScriptEditorVariableEdit; @@ -62,15 +62,8 @@ class VisualScriptEditor : public ScriptEditorBase { enum PortAction { - CREATE_CALL, - CREATE_SET, - CREATE_GET, - CREATE_COND, - CREATE_SEQUENCE, - CREATE_SWITCH, - CREATE_ITERATOR, - CREATE_WHILE, - CREATE_RETURN, + CREATE_CALL_SET_GET, + CREATE_ACTION, }; enum MemberAction { @@ -102,9 +95,9 @@ class VisualScriptEditor : public ScriptEditorBase { AcceptDialog *edit_signal_dialog; PropertyEditor *edit_signal_edit; - PropertySelector *method_select; - PropertySelector *new_connect_node_select; - PropertySelector *new_virtual_method_select; + VisualScriptPropertySelector *method_select; + VisualScriptPropertySelector *new_connect_node_select; + VisualScriptPropertySelector *new_virtual_method_select; VisualScriptEditorVariableEdit *variable_editor; @@ -162,21 +155,29 @@ class VisualScriptEditor : public ScriptEditorBase { static Clipboard *clipboard; - PopupMenu *port_action_popup; PopupMenu *member_popup; - MemberType member_type; String member_name; + bool seq_connect = false; + PortAction port_action; int port_action_node; int port_action_output; Vector2 port_action_pos; int port_action_new_node; void _port_action_menu(int p_option); - void _selected_connect_node_method_or_setget(const String &p_text); - void _cancel_connect_node_method_or_setget(); - void _selected_new_virtual_method(const String &p_text); + + void new_node(Ref<VisualScriptNode> vnode, Vector2 ofs); + + void connect_data(Ref<VisualScriptNode> vnode_old, Ref<VisualScriptNode> vnode, int new_id); + + void _selected_connect_node(const String &p_text, const String &p_category); + void connect_seq(Ref<VisualScriptNode> vnode_old, Ref<VisualScriptNode> vnode_new, int new_id); + + void _cancel_connect_node(); + void _create_new_node(const String &p_text, const String &p_category, const Vector2 &p_point); + void _selected_new_virtual_method(const String &p_text, const String &p_category); int error_line; @@ -231,7 +232,7 @@ class VisualScriptEditor : public ScriptEditorBase { void _comment_node_resized(const Vector2 &p_new_size, int p_node); int selecting_method_id; - void _selected_method(const String &p_method); + void _selected_method(const String &p_method, const String &p_type); void _draw_color_over_button(Object *obj, Color p_color); void _button_resource_previewed(const String &p_path, const Ref<Texture> &p_preview, Variant p_ud); diff --git a/modules/visual_script/visual_script_flow_control.cpp b/modules/visual_script/visual_script_flow_control.cpp index ea23ab1b2a..0f58a20c00 100644 --- a/modules/visual_script/visual_script_flow_control.cpp +++ b/modules/visual_script/visual_script_flow_control.cpp @@ -767,7 +767,7 @@ PropertyInfo VisualScriptTypeCast::get_input_value_port_info(int p_idx) const { PropertyInfo VisualScriptTypeCast::get_output_value_port_info(int p_idx) const { - return PropertyInfo(Variant::OBJECT, ""); + return PropertyInfo(Variant::OBJECT, "", PROPERTY_HINT_TYPE_STRING, get_base_type()); } String VisualScriptTypeCast::get_caption() const { diff --git a/modules/visual_script/visual_script_func_nodes.cpp b/modules/visual_script/visual_script_func_nodes.cpp index bdf5705ecd..ad886bc758 100644 --- a/modules/visual_script/visual_script_func_nodes.cpp +++ b/modules/visual_script/visual_script_func_nodes.cpp @@ -43,7 +43,7 @@ int VisualScriptFunctionCall::get_output_sequence_port_count() const { - if (method_cache.flags & METHOD_FLAG_CONST || (call_mode == CALL_MODE_BASIC_TYPE && Variant::is_method_const(basic_type, function))) + if ((method_cache.flags & METHOD_FLAG_CONST && call_mode != CALL_MODE_INSTANCE) || (call_mode == CALL_MODE_BASIC_TYPE && Variant::is_method_const(basic_type, function))) return 0; else return 1; @@ -51,7 +51,7 @@ int VisualScriptFunctionCall::get_output_sequence_port_count() const { bool VisualScriptFunctionCall::has_input_sequence_port() const { - if (method_cache.flags & METHOD_FLAG_CONST || (call_mode == CALL_MODE_BASIC_TYPE && Variant::is_method_const(basic_type, function))) + if ((method_cache.flags & METHOD_FLAG_CONST && call_mode != CALL_MODE_INSTANCE) || (call_mode == CALL_MODE_BASIC_TYPE && Variant::is_method_const(basic_type, function))) return false; else return true; @@ -231,7 +231,7 @@ PropertyInfo VisualScriptFunctionCall::get_output_value_port_info(int p_idx) con if (call_mode == CALL_MODE_INSTANCE) { if (p_idx == 0) { - return PropertyInfo(Variant::OBJECT, "pass"); + return PropertyInfo(Variant::OBJECT, "pass", PROPERTY_HINT_TYPE_STRING, get_base_type()); } else { p_idx--; } @@ -1055,7 +1055,7 @@ PropertyInfo VisualScriptPropertySet::get_output_value_port_info(int p_idx) cons if (call_mode == CALL_MODE_BASIC_TYPE) { return PropertyInfo(basic_type, "out"); } else if (call_mode == CALL_MODE_INSTANCE) { - return PropertyInfo(Variant::OBJECT, "pass"); + return PropertyInfo(Variant::OBJECT, "pass", PROPERTY_HINT_TYPE_STRING, get_base_type()); } else { return PropertyInfo(); } diff --git a/modules/visual_script/visual_script_nodes.cpp b/modules/visual_script/visual_script_nodes.cpp index 8b7b809ec0..f174300a4c 100644 --- a/modules/visual_script/visual_script_nodes.cpp +++ b/modules/visual_script/visual_script_nodes.cpp @@ -2198,7 +2198,7 @@ PropertyInfo VisualScriptSceneTree::get_input_value_port_info(int p_idx) const { PropertyInfo VisualScriptSceneTree::get_output_value_port_info(int p_idx) const { - return PropertyInfo(Variant::OBJECT, "Scene Tree"); + return PropertyInfo(Variant::OBJECT, "Scene Tree", PROPERTY_HINT_TYPE_STRING, "SceneTree"); } String VisualScriptSceneTree::get_caption() const { diff --git a/modules/visual_script/visual_script_property_selector.cpp b/modules/visual_script/visual_script_property_selector.cpp new file mode 100644 index 0000000000..994ea1f791 --- /dev/null +++ b/modules/visual_script/visual_script_property_selector.cpp @@ -0,0 +1,705 @@ +/*************************************************************************/ +/* visual_script_property_selector.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "visual_script_property_selector.h" + +#include "editor_scale.h" +#include "modules/visual_script/visual_script.h" +#include "modules/visual_script/visual_script_builtin_funcs.h" +#include "modules/visual_script/visual_script_flow_control.h" +#include "modules/visual_script/visual_script_func_nodes.h" +#include "modules/visual_script/visual_script_nodes.h" +#include "os/keyboard.h" +#include "scene/main/node.h" +#include "scene/main/viewport.h" + +void VisualScriptPropertySelector::_text_changed(const String &p_newtext) { + _update_search(); +} + +void VisualScriptPropertySelector::_sbox_input(const Ref<InputEvent> &p_ie) { + + Ref<InputEventKey> k = p_ie; + + if (k.is_valid()) { + + switch (k->get_scancode()) { + case KEY_UP: + case KEY_DOWN: + case KEY_PAGEUP: + case KEY_PAGEDOWN: { + + search_options->call("_gui_input", k); + search_box->accept_event(); + + TreeItem *root = search_options->get_root(); + if (!root->get_children()) + break; + + TreeItem *current = search_options->get_selected(); + + TreeItem *item = search_options->get_next_selected(root); + while (item) { + item->deselect(0); + item = search_options->get_next_selected(item); + } + + current->select(0); + + } break; + } + } +} + +void VisualScriptPropertySelector::_update_search() { + set_title(TTR("Search VisualScript")); + + search_options->clear(); + help_bit->set_text(""); + + TreeItem *root = search_options->create_item(); + bool found = false; + + if (properties) { + + List<PropertyInfo> props; + + if (instance) { + instance->get_property_list(&props, true); + } else if (type != Variant::NIL) { + Variant v; + Variant::CallError ce; + v = Variant::construct(type, NULL, 0, ce); + + v.get_property_list(&props); + } else { + + Object *obj = ObjectDB::get_instance(script); + if (Object::cast_to<Script>(obj)) { + + props.push_back(PropertyInfo(Variant::NIL, "Script Variables", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_CATEGORY)); + Object::cast_to<Script>(obj)->get_script_property_list(&props); + } + + StringName base = base_type; + while (base) { + props.push_back(PropertyInfo(Variant::NIL, base, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_CATEGORY)); + ClassDB::get_property_list(base, &props, true); + base = ClassDB::get_parent_class_nocheck(base); + } + } + + TreeItem *category = NULL; + + Ref<Texture> type_icons[Variant::VARIANT_MAX] = { + Control::get_icon("Variant", "EditorIcons"), + Control::get_icon("bool", "EditorIcons"), + Control::get_icon("int", "EditorIcons"), + Control::get_icon("float", "EditorIcons"), + Control::get_icon("String", "EditorIcons"), + Control::get_icon("Vector2", "EditorIcons"), + Control::get_icon("Rect2", "EditorIcons"), + Control::get_icon("Vector3", "EditorIcons"), + Control::get_icon("Transform2D", "EditorIcons"), + Control::get_icon("Plane", "EditorIcons"), + Control::get_icon("Quat", "EditorIcons"), + Control::get_icon("AABB", "EditorIcons"), + Control::get_icon("Basis", "EditorIcons"), + Control::get_icon("Transform", "EditorIcons"), + Control::get_icon("Color", "EditorIcons"), + Control::get_icon("Path", "EditorIcons"), + Control::get_icon("RID", "EditorIcons"), + Control::get_icon("Object", "EditorIcons"), + Control::get_icon("Dictionary", "EditorIcons"), + Control::get_icon("Array", "EditorIcons"), + Control::get_icon("PoolByteArray", "EditorIcons"), + Control::get_icon("PoolIntArray", "EditorIcons"), + Control::get_icon("PoolRealArray", "EditorIcons"), + Control::get_icon("PoolStringArray", "EditorIcons"), + Control::get_icon("PoolVector2Array", "EditorIcons"), + Control::get_icon("PoolVector3Array", "EditorIcons"), + Control::get_icon("PoolColorArray", "EditorIcons") + }; + + if (!seq_connect && visual_script_generic == false) { + get_visual_node_names("flow_control/type_cast", Set<String>(), found, root, search_box); + get_visual_node_names("functions/built_in/print", Set<String>(), found, root, search_box); + get_visual_node_names("functions/by_type/" + Variant::get_type_name(type), Set<String>(), found, root, search_box); + get_visual_node_names("operators/compare/", Set<String>(), found, root, search_box); + if (type == Variant::INT) { + get_visual_node_names("operators/bitwise/", Set<String>(), found, root, search_box); + } + if (type == Variant::BOOL) { + get_visual_node_names("operators/logic/", Set<String>(), found, root, search_box); + } + if (type == Variant::BOOL || type == Variant::INT || type == Variant::REAL || type == Variant::VECTOR2 || type == Variant::VECTOR3) { + get_visual_node_names("operators/math/", Set<String>(), found, root, search_box); + } + } + + for (List<PropertyInfo>::Element *E = props.front(); E; E = E->next()) { + if (E->get().usage == PROPERTY_USAGE_CATEGORY) { + if (category && category->get_children() == NULL) { + memdelete(category); //old category was unused + } + category = search_options->create_item(root); + category->set_text(0, E->get().name); + category->set_selectable(0, false); + + Ref<Texture> icon; + if (E->get().name == "Script Variables") { + icon = get_icon("Script", "EditorIcons"); + } else if (has_icon(E->get().name, "EditorIcons")) { + icon = get_icon(E->get().name, "EditorIcons"); + } else { + icon = get_icon("Object", "EditorIcons"); + } + category->set_icon(0, icon); + continue; + } + + if (!(E->get().usage & PROPERTY_USAGE_EDITOR) && !(E->get().usage & PROPERTY_USAGE_SCRIPT_VARIABLE)) + continue; + + if (type_filter.size() && type_filter.find(E->get().type) == -1) + continue; + + String get_text_raw = String(TTR("Get")) + String(" ") + E->get().name; + String get_text = get_text_raw.capitalize(); + + String set_text_raw = String(TTR("Set ")) + String(" ") + E->get().name; + String set_text = set_text_raw.capitalize(); + String input = search_box->get_text().capitalize(); + if (input == String() || + get_text_raw.findn(input) != -1 || + get_text.findn(input) != -1) { + TreeItem *item = search_options->create_item(category ? category : root); + item->set_text(0, get_text); + item->set_metadata(0, E->get().name); + item->set_icon(0, type_icons[E->get().type]); + item->set_metadata(1, "get"); + item->set_collapsed(1); + item->set_selectable(1, false); + item->set_selectable(0, true); + } + + if (input == String() || + set_text_raw.findn(input) != -1 && + set_text.findn(input) != -1) { + TreeItem *item = search_options->create_item(category ? category : root); + item->set_text(0, set_text); + item->set_metadata(0, E->get().name); + item->set_icon(0, type_icons[E->get().type]); + item->set_metadata(1, "set"); + item->set_selectable(1, false); + item->set_selectable(0, true); + } + } + } + + if (seq_connect == true && visual_script_generic == false) { + String text = search_box->get_text(); + create_visualscript_item(String("VisualScriptCondition"), root, text, String("Condition")); + create_visualscript_item(String("VisualScriptSwitch"), root, text, String("Switch")); + create_visualscript_item(String("VisualScriptSequence"), root, text, String("Sequence")); + create_visualscript_item(String("VisualScriptIterator"), root, text, String("Iterator")); + create_visualscript_item(String("VisualScriptWhile"), root, text, String("While")); + create_visualscript_item(String("VisualScriptReturn"), root, text, String("Return")); + get_visual_node_names("flow_control/type_cast", Set<String>(), found, root, search_box); + get_visual_node_names("functions/built_in/print", Set<String>(), found, root, search_box); + } + + if (visual_script_generic) { + get_visual_node_names("", Set<String>(), found, root, search_box); + } + + List<MethodInfo> methods; + + if (type != Variant::NIL) { + Variant v; + Variant::CallError ce; + v = Variant::construct(type, NULL, 0, ce); + v.get_method_list(&methods); + } else { + + Object *obj = ObjectDB::get_instance(script); + if (Object::cast_to<Script>(obj)) { + + methods.push_back(MethodInfo("*Script Methods")); + Object::cast_to<Script>(obj)->get_script_method_list(&methods); + } + + StringName base = base_type; + while (base) { + methods.push_back(MethodInfo("*" + String(base))); + ClassDB::get_method_list(base, &methods, true, true); + base = ClassDB::get_parent_class_nocheck(base); + } + } + TreeItem *category = NULL; + bool script_methods = false; + + for (List<MethodInfo>::Element *E = methods.front(); E; E = E->next()) { + if (E->get().name.begins_with("*")) { + if (category && category->get_children() == NULL) { + memdelete(category); //old category was unused + } + category = search_options->create_item(root); + category->set_text(0, E->get().name.replace_first("*", "")); + category->set_selectable(0, false); + + Ref<Texture> icon; + script_methods = false; + print_line("name: " + E->get().name); + String rep = E->get().name.replace("*", ""); + if (E->get().name == "*Script Methods") { + icon = get_icon("Script", "EditorIcons"); + script_methods = true; + } else if (has_icon(rep, "EditorIcons")) { + icon = get_icon(rep, "EditorIcons"); + } else { + icon = get_icon("Object", "EditorIcons"); + } + category->set_icon(0, icon); + + continue; + } + + String name = E->get().name.get_slice(":", 0); + if (!script_methods && name.begins_with("_") && !(E->get().flags & METHOD_FLAG_VIRTUAL)) + continue; + + if (virtuals_only && !(E->get().flags & METHOD_FLAG_VIRTUAL)) + continue; + + if (!virtuals_only && (E->get().flags & METHOD_FLAG_VIRTUAL)) + continue; + + MethodInfo mi = E->get(); + String desc = mi.name.capitalize() + " ("; + + if (search_box->get_text() != String() && + name.findn(search_box->get_text()) == -1 && + desc.findn(search_box->get_text()) == -1) + continue; + + TreeItem *item = search_options->create_item(category ? category : root); + + for (int i = 0; i < mi.arguments.size(); i++) { + + if (i > 0) + desc += ", "; + + if (mi.arguments[i].type == Variant::NIL) + desc += "var"; + else if (mi.arguments[i].name.find(":") != -1) { + desc += mi.arguments[i].name.get_slice(":", 1); + mi.arguments[i].name = mi.arguments[i].name.get_slice(":", 0); + } else + desc += Variant::get_type_name(mi.arguments[i].type); + } + + desc += ")"; + + item->set_text(0, desc); + item->set_icon(0, get_icon("MemberMethod", "EditorIcons")); + item->set_metadata(0, name); + item->set_selectable(0, true); + + item->set_metadata(1, "method"); + item->set_collapsed(1); + item->set_selectable(1, false); + + if (category && category->get_children() == NULL) { + memdelete(category); //old category was unused + } + } + + TreeItem *selected_item = search_options->search_item_text(search_box->get_text()); + if (!found && selected_item != NULL) { + selected_item->select(0); + found = true; + } + + get_ok()->set_disabled(root->get_children() == NULL); +} + +void VisualScriptPropertySelector::create_visualscript_item(const String &name, TreeItem *const root, const String &search_input, const String &text) { + if (search_input == String() || text.findn(search_input) != -1) { + TreeItem *item = search_options->create_item(root); + item->set_text(0, text); + item->set_icon(0, get_icon("VisualScript", "EditorIcons")); + item->set_metadata(0, name); + item->set_metadata(1, "action"); + item->set_selectable(0, true); + item->set_collapsed(1); + item->set_selectable(1, false); + } +} + +void VisualScriptPropertySelector::get_visual_node_names(const String &root_filter, const Set<String> &filter, bool &found, TreeItem *const root, LineEdit *const search_box) { + Map<String, TreeItem *> path_cache; + + List<String> fnodes; + VisualScriptLanguage::singleton->get_registered_node_names(&fnodes); + + for (List<String>::Element *E = fnodes.front(); E; E = E->next()) { + if (!E->get().begins_with(root_filter)) { + continue; + } + Vector<String> path = E->get().split("/"); + bool is_filter = false; + for (Set<String>::Element *E = filter.front(); E; E = E->next()) { + if (path.size() >= 2 && path[1].findn(E->get()) != -1) { + is_filter = true; + break; + } + } + if (is_filter == true) { + continue; + } + + if (search_box->get_text() != String() && E->get().findn(search_box->get_text()) == -1) { + continue; + } + TreeItem *item = search_options->create_item(root); + VisualScriptOperator *vnode_operator = Object::cast_to<VisualScriptOperator>(*VisualScriptLanguage::singleton->create_node_from_name(E->get())); + String type_name; + if (vnode_operator != NULL) { + String type; + if (path.size() >= 2) { + type = path[1]; + } + type_name = type.capitalize() + " "; + } + VisualScriptFunctionCall *vnode_function_call = Object::cast_to<VisualScriptFunctionCall>(*VisualScriptLanguage::singleton->create_node_from_name(E->get())); + if (vnode_function_call != NULL) { + String basic_type = Variant::get_type_name(vnode_function_call->get_basic_type()); + type_name = basic_type.capitalize() + " "; + } + VisualScriptBuiltinFunc *vnode_builtin_function_call = Object::cast_to<VisualScriptBuiltinFunc>(*VisualScriptLanguage::singleton->create_node_from_name(E->get())); + if (vnode_builtin_function_call != NULL) { + type_name = "Builtin "; + } + item->set_text(0, type_name + path[path.size() - 1].capitalize()); + item->set_icon(0, get_icon("VisualScript", "EditorIcons")); + item->set_selectable(0, true); + item->set_metadata(0, E->get()); + item->set_selectable(0, true); + item->set_metadata(1, "visualscript"); + item->set_selectable(1, false); + } +} + +void VisualScriptPropertySelector::_confirmed() { + + TreeItem *ti = search_options->get_selected(); + if (!ti) + return; + emit_signal("selected", ti->get_metadata(0), ti->get_metadata(1)); + hide(); +} + +void VisualScriptPropertySelector::_item_selected() { + + help_bit->set_text(""); + + TreeItem *item = search_options->get_selected(); + if (!item) + return; + String name = item->get_metadata(0); + + String class_type; + if (type) { + class_type = Variant::get_type_name(type); + + } else { + class_type = base_type; + } + + DocData *dd = EditorHelp::get_doc_data(); + String text; + + String at_class = class_type; + + while (at_class != String()) { + + Map<String, DocData::ClassDoc>::Element *E = dd->class_list.find(at_class); + if (E) { + for (int i = 0; i < E->get().properties.size(); i++) { + if (E->get().properties[i].name == name) { + text = E->get().properties[i].description; + } + } + } + + at_class = ClassDB::get_parent_class_nocheck(at_class); + } + at_class = class_type; + + while (at_class != String()) { + + Map<String, DocData::ClassDoc>::Element *E = dd->class_list.find(at_class); + if (E) { + for (int i = 0; i < E->get().methods.size(); i++) { + if (E->get().methods[i].name == name) { + text = E->get().methods[i].description; + } + } + } + + at_class = ClassDB::get_parent_class_nocheck(at_class); + } + Map<String, DocData::ClassDoc>::Element *E = dd->class_list.find(class_type); + if (E) { + for (int i = 0; i < E->get().methods.size(); i++) { + Vector<String> functions = name.rsplit("/", false, 1); + if (E->get().methods[i].name == functions[functions.size() - 1]) { + text = E->get().methods[i].description; + } + } + } + + List<String> *names = memnew(List<String>); + VisualScriptLanguage::singleton->get_registered_node_names(names); + if (names->find(name) != NULL) { + Ref<VisualScriptOperator> operator_node = VisualScriptLanguage::singleton->create_node_from_name(name); + if (operator_node.is_valid()) { + Map<String, DocData::ClassDoc>::Element *E = dd->class_list.find(operator_node->get_class_name()); + if (E) { + text = Variant::get_operator_name(operator_node->get_operator()); + } + } + Ref<VisualScriptTypeCast> typecast_node = VisualScriptLanguage::singleton->create_node_from_name(name); + if (typecast_node.is_valid()) { + Map<String, DocData::ClassDoc>::Element *E = dd->class_list.find(typecast_node->get_class_name()); + if (E) { + text = E->get().description; + } + } + + Ref<VisualScriptBuiltinFunc> builtin_node = VisualScriptLanguage::singleton->create_node_from_name(name); + if (builtin_node.is_valid()) { + Map<String, DocData::ClassDoc>::Element *E = dd->class_list.find(builtin_node->get_class_name()); + if (E) { + for (int i = 0; i < E->get().constants.size(); i++) { + if (E->get().constants[i].value.to_int() == int(builtin_node->get_func())) { + text = E->get().constants[i].description; + } + } + } + } + } + + memdelete(names); + + if (text == String()) + return; + + help_bit->set_text(text); +} + +void VisualScriptPropertySelector::_notification(int p_what) { + + if (p_what == NOTIFICATION_ENTER_TREE) { + + connect("confirmed", this, "_confirmed"); + } +} + +void VisualScriptPropertySelector::select_method_from_base_type(const String &p_base, const String &p_current, bool p_virtuals_only) { + + base_type = p_base; + selected = p_current; + type = Variant::NIL; + script = 0; + properties = false; + instance = NULL; + virtuals_only = p_virtuals_only; + + show_window(.5f); + search_box->set_text(""); + search_box->grab_focus(); + _update_search(); +} + +void VisualScriptPropertySelector::set_type_filter(const Vector<Variant::Type> &p_type_filter) { + type_filter = p_type_filter; +} + +void VisualScriptPropertySelector::select_from_base_type(const String &p_base, const String &p_current /*= ""*/, bool p_virtuals_only /*= false*/, bool p_seq_connect /*= false*/) { + + base_type = p_base; + selected = p_current; + type = Variant::NIL; + script = 0; + properties = true; + instance = NULL; + virtuals_only = p_virtuals_only; + + show_window(.5f); + search_box->set_text(""); + search_box->grab_focus(); + seq_connect = p_seq_connect; + + _update_search(); +} + +void VisualScriptPropertySelector::select_from_script(const Ref<Script> &p_script, const String &p_current /*= ""*/) { + ERR_FAIL_COND(p_script.is_null()); + + base_type = p_script->get_instance_base_type(); + selected = p_current; + type = Variant::NIL; + script = p_script->get_instance_id(); + properties = true; + instance = NULL; + virtuals_only = false; + + show_window(.5f); + search_box->set_text(""); + search_box->grab_focus(); + seq_connect = false; + + _update_search(); +} + +void VisualScriptPropertySelector::select_from_basic_type(Variant::Type p_type, const String &p_current /*= ""*/) { + ERR_FAIL_COND(p_type == Variant::NIL); + base_type = ""; + selected = p_current; + type = p_type; + script = 0; + properties = true; + instance = NULL; + virtuals_only = false; + + show_window(.5f); + search_box->set_text(""); + search_box->grab_focus(); + seq_connect = false; + + _update_search(); +} + +void VisualScriptPropertySelector::select_from_action(const String &p_type, const String &p_current /*= ""*/) { + base_type = p_type; + selected = p_current; + type = Variant::NIL; + script = 0; + properties = false; + instance = NULL; + virtuals_only = false; + + show_window(.5f); + search_box->set_text(""); + search_box->grab_focus(); + seq_connect = true; + _update_search(); +} + +void VisualScriptPropertySelector::select_from_instance(Object *p_instance, const String &p_current /*= ""*/) { + base_type = ""; + selected = p_current; + type = Variant::NIL; + script = 0; + properties = true; + instance = p_instance; + virtuals_only = false; + + show_window(.5f); + search_box->set_text(""); + search_box->grab_focus(); + seq_connect = false; + + _update_search(); +} + +void VisualScriptPropertySelector::select_from_visual_script(const String &p_base) { + base_type = p_base; + selected = ""; + type = Variant::NIL; + script = 0; + properties = true; + visual_script_generic = true; + instance = NULL; + virtuals_only = false; + show_window(.5f); + search_box->set_text(""); + search_box->grab_focus(); + + _update_search(); +} + +void VisualScriptPropertySelector::show_window(float p_screen_ratio) { + Rect2 rect; + Point2 window_size = get_viewport_rect().size; + rect.size = (window_size * p_screen_ratio).floor(); + rect.size.x = rect.size.x / 1.25f; + rect.position = ((window_size - rect.size) / 2.0f).floor(); + popup(rect); +} + +void VisualScriptPropertySelector::_bind_methods() { + + ClassDB::bind_method(D_METHOD("_text_changed"), &VisualScriptPropertySelector::_text_changed); + ClassDB::bind_method(D_METHOD("_confirmed"), &VisualScriptPropertySelector::_confirmed); + ClassDB::bind_method(D_METHOD("_sbox_input"), &VisualScriptPropertySelector::_sbox_input); + ClassDB::bind_method(D_METHOD("_item_selected"), &VisualScriptPropertySelector::_item_selected); + + ADD_SIGNAL(MethodInfo("selected", PropertyInfo(Variant::STRING, "name"))); +} + +VisualScriptPropertySelector::VisualScriptPropertySelector() { + + VBoxContainer *vbc = memnew(VBoxContainer); + add_child(vbc); + //set_child_rect(vbc); + search_box = memnew(LineEdit); + vbc->add_margin_child(TTR("Search:"), search_box); + search_box->connect("text_changed", this, "_text_changed"); + search_box->connect("gui_input", this, "_sbox_input"); + search_options = memnew(Tree); + vbc->add_margin_child(TTR("Matches:"), search_options, true); + get_ok()->set_text(TTR("Open")); + get_ok()->set_disabled(true); + register_text_enter(search_box); + set_hide_on_ok(false); + search_options->connect("item_activated", this, "_confirmed"); + search_options->connect("cell_selected", this, "_item_selected"); + search_options->set_hide_root(true); + search_options->set_hide_folding(true); + virtuals_only = false; + help_bit = memnew(EditorHelpBit); + vbc->add_margin_child(TTR("Description:"), help_bit); + help_bit->connect("request_hide", this, "_closed"); + search_options->set_columns(2); + search_options->set_column_expand(1, false); +} diff --git a/modules/visual_script/visual_script_property_selector.h b/modules/visual_script/visual_script_property_selector.h new file mode 100644 index 0000000000..ec536f86a8 --- /dev/null +++ b/modules/visual_script/visual_script_property_selector.h @@ -0,0 +1,92 @@ +/*************************************************************************/ +/* visual_script_property_selector.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef VISUALSCRIPT_PROPERTYSELECTOR_H +#define VISUALSCRIPT_PROPERTYSELECTOR_H + +#include "editor/property_editor.h" +#include "editor_help.h" +#include "scene/gui/rich_text_label.h" + +class VisualScriptPropertySelector : public ConfirmationDialog { + GDCLASS(VisualScriptPropertySelector, ConfirmationDialog) + + LineEdit *search_box; + Tree *search_options; + + void _update_search(); + + void create_visualscript_item(const String &name, TreeItem *const root, const String &search_input, const String &text); + + void get_visual_node_names(const String &root_filter, const Set<String> &filter, bool &found, TreeItem *const root, LineEdit *const search_box); + + void _sbox_input(const Ref<InputEvent> &p_ie); + + void _confirmed(); + void _text_changed(const String &p_newtext); + + EditorHelpBit *help_bit; + + bool properties; + bool visual_script_generic; + String selected; + Variant::Type type; + String base_type; + ObjectID script; + Object *instance; + bool virtuals_only; + + bool seq_connect = false; + + void _item_selected(); + + Vector<Variant::Type> type_filter; + +protected: + void _notification(int p_what); + static void _bind_methods(); + +public: + void select_method_from_base_type(const String &p_base, const String &p_current = "", bool p_virtuals_only = false); + void select_from_base_type(const String &p_base, const String &p_current = "", bool p_virtuals_only = false, bool p_seq_connect = false); + void select_from_script(const Ref<Script> &p_script, const String &p_current /*= ""*/); + void select_from_basic_type(Variant::Type p_type, const String &p_current = ""); + void select_from_action(const String &p_type, const String &p_current = ""); + void select_from_instance(Object *p_instance, const String &p_current = ""); + void select_from_visual_script(const String &p_base); + + void show_window(float p_screen_ratio); + + void set_type_filter(const Vector<Variant::Type> &p_type_filter); + + VisualScriptPropertySelector(); +}; + +#endif // VISUALSCRIPT_PROPERTYSELECTOR_H diff --git a/scene/gui/graph_edit.cpp b/scene/gui/graph_edit.cpp index e2c730a56e..d95ec9e495 100644 --- a/scene/gui/graph_edit.cpp +++ b/scene/gui/graph_edit.cpp @@ -268,6 +268,10 @@ void GraphEdit::remove_child_notify(Node *p_child) { void GraphEdit::_notification(int p_what) { + if (p_what == NOTIFICATION_ENTER_TREE || p_what == NOTIFICATION_THEME_CHANGED) { + port_grab_distance_horizontal = get_constant("port_grab_distance_horizontal"); + port_grab_distance_vertical = get_constant("port_grab_distance_vertical"); + } if (p_what == NOTIFICATION_READY) { Size2 hmin = h_scroll->get_combined_minimum_size(); Size2 vmin = v_scroll->get_combined_minimum_size(); @@ -343,8 +347,6 @@ bool GraphEdit::_filter_input(const Point2 &p_point) { Ref<Texture> port = get_icon("port", "GraphNode"); - float grab_r_extend = 2.0; - float grab_r = port->get_width() * 0.5 * grab_r_extend; for (int i = get_child_count() - 1; i >= 0; i--) { GraphNode *gn = Object::cast_to<GraphNode>(get_child(i)); @@ -354,14 +356,14 @@ bool GraphEdit::_filter_input(const Point2 &p_point) { for (int j = 0; j < gn->get_connection_output_count(); j++) { Vector2 pos = gn->get_connection_output_position(j) + gn->get_position(); - if (pos.distance_to(p_point) < grab_r) + if (create_hot_zone(pos).has_point(p_point)) return true; } for (int j = 0; j < gn->get_connection_input_count(); j++) { Vector2 pos = gn->get_connection_input_position(j) + gn->get_position(); - if (pos.distance_to(p_point) < grab_r) { + if (create_hot_zone(pos).has_point(p_point)) { return true; } } @@ -372,13 +374,11 @@ bool GraphEdit::_filter_input(const Point2 &p_point) { void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) { - float grab_r_extend = 2.0; Ref<InputEventMouseButton> mb = p_ev; if (mb.is_valid() && mb->get_button_index() == BUTTON_LEFT && mb->is_pressed()) { Ref<Texture> port = get_icon("port", "GraphNode"); Vector2 mpos(mb->get_position().x, mb->get_position().y); - float grab_r = port->get_width() * 0.5 * grab_r_extend; for (int i = get_child_count() - 1; i >= 0; i--) { GraphNode *gn = Object::cast_to<GraphNode>(get_child(i)); @@ -388,7 +388,7 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) { for (int j = 0; j < gn->get_connection_output_count(); j++) { Vector2 pos = gn->get_connection_output_position(j) + gn->get_position(); - if (pos.distance_to(mpos) < grab_r) { + if (create_hot_zone(pos).has_point(mpos)) { if (valid_left_disconnect_types.has(gn->get_connection_output_type(j))) { //check disconnect @@ -435,8 +435,7 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) { for (int j = 0; j < gn->get_connection_input_count(); j++) { Vector2 pos = gn->get_connection_input_position(j) + gn->get_position(); - - if (pos.distance_to(mpos) < grab_r) { + if (create_hot_zone(pos).has_point(mpos)) { if (right_disconnects || valid_right_disconnect_types.has(gn->get_connection_input_type(j))) { //check disconnect @@ -492,7 +491,6 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) { Ref<Texture> port = get_icon("port", "GraphNode"); Vector2 mpos = mm->get_position(); - float grab_r = port->get_width() * 0.5 * grab_r_extend; for (int i = get_child_count() - 1; i >= 0; i--) { GraphNode *gn = Object::cast_to<GraphNode>(get_child(i)); @@ -504,7 +502,7 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) { Vector2 pos = gn->get_connection_output_position(j) + gn->get_position(); int type = gn->get_connection_output_type(j); - if ((type == connecting_type || valid_connection_types.has(ConnType(type, connecting_type))) && pos.distance_to(mpos) < grab_r) { + if ((type == connecting_type || valid_connection_types.has(ConnType(type, connecting_type))) && create_hot_zone(pos).has_point(mpos)) { connecting_target = true; connecting_to = pos; @@ -519,7 +517,7 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) { Vector2 pos = gn->get_connection_input_position(j) + gn->get_position(); int type = gn->get_connection_input_type(j); - if ((type == connecting_type || valid_connection_types.has(ConnType(type, connecting_type))) && pos.distance_to(mpos) < grab_r) { + if ((type == connecting_type || valid_connection_types.has(ConnType(type, connecting_type))) && create_hot_zone(pos).has_point(mpos)) { connecting_target = true; connecting_to = pos; connecting_target_to = gn->get_name(); @@ -559,6 +557,10 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) { } } +Rect2 GraphEdit::create_hot_zone(const Vector2 &pos) { + return Rect2(pos.x - port_grab_distance_horizontal, pos.y - port_grab_distance_vertical, port_grab_distance_horizontal * 2, port_grab_distance_vertical * 2); +} + template <class Vector2> static _FORCE_INLINE_ Vector2 _bezier_interp(real_t t, Vector2 start, Vector2 control_1, Vector2 control_2, Vector2 end) { /* Formula from Wikipedia article on Bezier curves. */ diff --git a/scene/gui/graph_edit.h b/scene/gui/graph_edit.h index 14789001e4..64ba18681e 100644 --- a/scene/gui/graph_edit.h +++ b/scene/gui/graph_edit.h @@ -81,6 +81,9 @@ private: HScrollBar *h_scroll; VScrollBar *v_scroll; + float port_grab_distance_horizontal; + float port_grab_distance_vertical; + bool connecting; String connecting_from; bool connecting_out; @@ -127,6 +130,9 @@ private: Control *connections_layer; GraphEditFilter *top_layer; void _top_layer_input(const Ref<InputEvent> &p_ev); + + Rect2 create_hot_zone(const Vector2 &pos); + void _top_layer_draw(); void _connections_layer_draw(); void _update_scroll_offset(); diff --git a/scene/resources/default_theme/default_theme.cpp b/scene/resources/default_theme/default_theme.cpp index fe12e2f5f6..601f6fb558 100644 --- a/scene/resources/default_theme/default_theme.cpp +++ b/scene/resources/default_theme/default_theme.cpp @@ -883,6 +883,10 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_icon("logo", "Icons", make_icon(logo_png)); + // Visual Node Ports + theme->set_constant("port_grab_distance_horizontal", "GraphEdit", 48 * scale); + theme->set_constant("port_grab_distance_vertical", "GraphEdit", 6 * scale); + // Theme default_icon = make_icon(error_icon_png); |