diff options
Diffstat (limited to 'editor/connections_dialog.cpp')
-rw-r--r-- | editor/connections_dialog.cpp | 337 |
1 files changed, 265 insertions, 72 deletions
diff --git a/editor/connections_dialog.cpp b/editor/connections_dialog.cpp index 9ff480f130..dee130ed0f 100644 --- a/editor/connections_dialog.cpp +++ b/editor/connections_dialog.cpp @@ -30,6 +30,7 @@ #include "connections_dialog.h" +#include "core/config/project_settings.h" #include "editor/doc_tools.h" #include "editor/editor_help.h" #include "editor/editor_node.h" @@ -165,6 +166,7 @@ void ConnectDialog::_tree_node_selected() { if (!edit_mode) { set_dst_method(generate_method_callback_name(source, signal, current)); } + _update_method_tree(); _update_ok_enabled(); } @@ -182,6 +184,11 @@ void ConnectDialog::_unbind_count_changed(double p_count) { } } +void ConnectDialog::_method_selected() { + TreeItem *selected_item = method_tree->get_selected(); + dst_method->set_text(selected_item->get_text(0)); +} + /* * Adds a new parameter bind to connection. */ @@ -242,14 +249,153 @@ StringName ConnectDialog::generate_method_callback_name(Node *p_source, String p String dst_method; if (p_source == p_target) { - dst_method = String(EDITOR_GET("interface/editors/default_signal_callback_to_self_name")).format(subst); + dst_method = String(GLOBAL_GET("editor/naming/default_signal_callback_to_self_name")).format(subst); } else { - dst_method = String(EDITOR_GET("interface/editors/default_signal_callback_name")).format(subst); + dst_method = String(GLOBAL_GET("editor/naming/default_signal_callback_name")).format(subst); } return dst_method; } +void ConnectDialog::_create_method_tree_items(const List<MethodInfo> &p_methods, TreeItem *p_parent_item) { + for (const MethodInfo &mi : p_methods) { + TreeItem *method_item = method_tree->create_item(p_parent_item); + method_item->set_text(0, mi.name); + if (mi.return_val.type == Variant::NIL) { + method_item->set_icon(0, get_theme_icon(SNAME("Variant"), "EditorIcons")); + } else { + method_item->set_icon(0, get_theme_icon(Variant::get_type_name(mi.return_val.type), "EditorIcons")); + } + } +} + +List<MethodInfo> ConnectDialog::_filter_method_list(const List<MethodInfo> &p_methods, const MethodInfo &p_signal, const String &p_search_string) const { + bool check_signal = compatible_methods_only->is_pressed(); + List<MethodInfo> ret; + + for (const MethodInfo &mi : p_methods) { + if (!p_search_string.is_empty() && !mi.name.contains(p_search_string)) { + continue; + } + + if (check_signal) { + if (mi.arguments.size() != p_signal.arguments.size()) { + continue; + } + + bool type_mismatch = false; + const List<PropertyInfo>::Element *E = p_signal.arguments.front(); + for (const List<PropertyInfo>::Element *F = mi.arguments.front(); F; F = F->next(), E = E->next()) { + Variant::Type stype = E->get().type; + Variant::Type mtype = F->get().type; + + if (stype != Variant::NIL && mtype != Variant::NIL && stype != mtype) { + type_mismatch = true; + break; + } + } + + if (type_mismatch) { + continue; + } + } + ret.push_back(mi); + } + return ret; +} + +void ConnectDialog::_update_method_tree() { + method_tree->clear(); + + Color disabled_color = get_theme_color(SNAME("accent_color"), SNAME("Editor")) * 0.7; + String search_string = method_search->get_text(); + Node *target = tree->get_selected(); + if (!target) { + return; + } + + MethodInfo signal_info; + if (compatible_methods_only->is_pressed()) { + List<MethodInfo> signals; + source->get_signal_list(&signals); + for (const MethodInfo &mi : signals) { + if (mi.name == signal) { + signal_info = mi; + break; + } + } + } + + TreeItem *root_item = method_tree->create_item(); + root_item->set_text(0, TTR("Methods")); + root_item->set_selectable(0, false); + + // If a script is attached, get methods from it. + ScriptInstance *si = target->get_script_instance(); + if (si) { + TreeItem *si_item = method_tree->create_item(root_item); + si_item->set_text(0, TTR("Attached Script")); + si_item->set_icon(0, get_theme_icon(SNAME("Script"), SNAME("EditorIcons"))); + si_item->set_selectable(0, false); + + List<MethodInfo> methods; + si->get_method_list(&methods); + methods = _filter_method_list(methods, signal_info, search_string); + + if (methods.is_empty()) { + si_item->set_custom_color(0, disabled_color); + } else { + _create_method_tree_items(methods, si_item); + } + } + + if (script_methods_only->is_pressed()) { + empty_tree_label->set_visible(root_item->get_first_child() == nullptr); + return; + } + + // Get methods from each class in the heirarchy. + StringName current_class = target->get_class_name(); + do { + TreeItem *class_item = method_tree->create_item(root_item); + class_item->set_text(0, current_class); + Ref<Texture2D> icon = get_theme_icon(SNAME("Node"), SNAME("EditorIcons")); + if (has_theme_icon(current_class, SNAME("EditorIcons"))) { + icon = get_theme_icon(current_class, SNAME("EditorIcons")); + } + class_item->set_icon(0, icon); + class_item->set_selectable(0, false); + + List<MethodInfo> methods; + ClassDB::get_method_list(current_class, &methods, true); + methods = _filter_method_list(methods, signal_info, search_string); + + if (methods.is_empty()) { + class_item->set_custom_color(0, disabled_color); + } else { + _create_method_tree_items(methods, class_item); + } + current_class = ClassDB::get_parent_class_nocheck(current_class); + } while (current_class != StringName()); + + empty_tree_label->set_visible(root_item->get_first_child() == nullptr); +} + +void ConnectDialog::_method_check_button_pressed(const CheckButton *p_button) { + if (p_button == script_methods_only) { + EditorSettings::get_singleton()->set_project_metadata("editor_metadata", "show_script_methods_only", p_button->is_pressed()); + } else if (p_button == compatible_methods_only) { + EditorSettings::get_singleton()->set_project_metadata("editor_metadata", "show_compatible_methods_only", p_button->is_pressed()); + } + _update_method_tree(); +} + +void ConnectDialog::_open_method_popup() { + method_popup->popup_centered(); + method_search->clear(); + method_search->grab_focus(); +} + /* * Enables or disables the connect button. The connect button is enabled if a * node is selected and valid in the selected mode. @@ -262,7 +408,7 @@ void ConnectDialog::_update_ok_enabled() { return; } - if (!advanced->is_pressed() && target->get_script().is_null()) { + if (dst_method->get_text().is_empty()) { get_ok_button()->set_disabled(true); return; } @@ -288,14 +434,13 @@ void ConnectDialog::_notification(int p_what) { style->set_content_margin(SIDE_TOP, style->get_content_margin(SIDE_TOP) + 1.0); from_signal->add_theme_style_override("normal", style); } + method_search->set_right_icon(get_theme_icon("Search", "EditorIcons")); + open_method_tree->set_icon(get_theme_icon("Edit", "EditorIcons")); } break; } } void ConnectDialog::_bind_methods() { - ClassDB::bind_method("_cancel", &ConnectDialog::_cancel_pressed); - ClassDB::bind_method("_update_ok_enabled", &ConnectDialog::_update_ok_enabled); - ADD_SIGNAL(MethodInfo("connected")); } @@ -303,10 +448,18 @@ Node *ConnectDialog::get_source() const { return source; } +ConnectDialog::ConnectionData ConnectDialog::get_source_connection_data() const { + return source_connection_data; +} + StringName ConnectDialog::get_signal_name() const { return signal; } +PackedStringArray ConnectDialog::get_signal_args() const { + return signal_args; +} + NodePath ConnectDialog::get_dst_path() const { return dst_path; } @@ -355,11 +508,12 @@ bool ConnectDialog::is_editing() const { * If creating a connection from scratch, sensible defaults are used. * If editing an existing connection, previous data is retained. */ -void ConnectDialog::init(ConnectionData p_cd, bool p_edit) { +void ConnectDialog::init(const ConnectionData &p_cd, const PackedStringArray &p_signal_args, bool p_edit) { set_hide_on_ok(false); source = static_cast<Node *>(p_cd.source); signal = p_cd.signal; + signal_args = p_signal_args; tree->set_selected(nullptr); tree->set_marked(source, true); @@ -377,22 +531,7 @@ void ConnectDialog::init(ConnectionData p_cd, bool p_edit) { deferred->set_pressed(b_deferred); one_shot->set_pressed(b_oneshot); - MethodInfo r_signal; - Ref<Script> source_script = source->get_script(); - if (source_script.is_valid() && source_script->has_script_signal(signal)) { - List<MethodInfo> signals; - source_script->get_script_signal_list(&signals); - for (MethodInfo &mi : signals) { - if (mi.name == signal) { - r_signal = mi; - break; - } - } - } else { - ClassDB::get_signal(source->get_class(), signal, &r_signal); - } - - unbind_count->set_max(r_signal.arguments.size()); + unbind_count->set_max(p_signal_args.size()); unbind_count->set_value(p_cd.unbinds); _unbind_count_changed(p_cd.unbinds); @@ -402,6 +541,8 @@ void ConnectDialog::init(ConnectionData p_cd, bool p_edit) { cdbinds->notify_changed(); edit_mode = p_edit; + + source_connection_data = p_cd; } void ConnectDialog::popup_dialog(const String &p_for_signal) { @@ -437,7 +578,6 @@ void ConnectDialog::_advanced_pressed() { error_label->set_visible(!_find_first_script(get_tree()->get_edited_scene_root(), get_tree()->get_edited_scene_root())); } - _update_ok_enabled(); EditorSettings::get_singleton()->set_project_metadata("editor_metadata", "use_advanced_connections", advanced->is_pressed()); popup_centered(); @@ -458,8 +598,8 @@ ConnectDialog::ConnectDialog() { vbc_left->set_h_size_flags(Control::SIZE_EXPAND_FILL); from_signal = memnew(LineEdit); - from_signal->set_editable(false); vbc_left->add_margin_child(TTR("From Signal:"), from_signal); + from_signal->set_editable(false); tree = memnew(SceneTreeEditor(false)); tree->set_connecting_signal(true); @@ -476,6 +616,43 @@ ConnectDialog::ConnectDialog() { vbc_left->add_child(error_label); error_label->hide(); + method_popup = memnew(AcceptDialog); + method_popup->set_title(TTR("Select Method")); + method_popup->set_min_size(Vector2(400, 600) * EDSCALE); + add_child(method_popup); + + VBoxContainer *method_vbc = memnew(VBoxContainer); + method_popup->add_child(method_vbc); + + method_search = memnew(LineEdit); + method_vbc->add_child(method_search); + method_search->set_placeholder(TTR("Filter Methods")); + method_search->set_clear_button_enabled(true); + method_search->connect("text_changed", callable_mp(this, &ConnectDialog::_update_method_tree).unbind(1)); + + method_tree = memnew(Tree); + method_vbc->add_child(method_tree); + method_tree->set_v_size_flags(Control::SIZE_EXPAND_FILL); + method_tree->set_hide_root(true); + method_tree->connect("item_selected", callable_mp(this, &ConnectDialog::_method_selected)); + method_tree->connect("item_activated", callable_mp((Window *)method_popup, &Window::hide)); + + empty_tree_label = memnew(Label(TTR("No method found matching given filters."))); + method_tree->add_child(empty_tree_label); + empty_tree_label->set_anchors_and_offsets_preset(Control::PRESET_CENTER); + + script_methods_only = memnew(CheckButton(TTR("Script Methods Only"))); + method_vbc->add_child(script_methods_only); + script_methods_only->set_h_size_flags(Control::SIZE_SHRINK_END); + script_methods_only->set_pressed(EditorSettings::get_singleton()->get_project_metadata("editor_metadata", "show_script_methods_only", true)); + script_methods_only->connect("pressed", callable_mp(this, &ConnectDialog::_method_check_button_pressed).bind(script_methods_only)); + + compatible_methods_only = memnew(CheckButton(TTR("Compatible Methods Only"))); + method_vbc->add_child(compatible_methods_only); + compatible_methods_only->set_h_size_flags(Control::SIZE_SHRINK_END); + compatible_methods_only->set_pressed(EditorSettings::get_singleton()->get_project_metadata("editor_metadata", "show_compatible_methods_only", true)); + compatible_methods_only->connect("pressed", callable_mp(this, &ConnectDialog::_method_check_button_pressed).bind(compatible_methods_only)); + vbc_right = memnew(VBoxContainer); main_hb->add_child(vbc_right); vbc_right->set_h_size_flags(Control::SIZE_EXPAND_FILL); @@ -521,14 +698,22 @@ ConnectDialog::ConnectDialog() { vbc_right->add_margin_child(TTR("Unbind Signal Arguments:"), unbind_count); + HBoxContainer *hbc_method = memnew(HBoxContainer); + vbc_left->add_margin_child(TTR("Receiver Method:"), hbc_method); + dst_method = memnew(LineEdit); dst_method->set_h_size_flags(Control::SIZE_EXPAND_FILL); + dst_method->connect("text_changed", callable_mp(method_tree, &Tree::deselect_all).unbind(1)); dst_method->connect("text_submitted", callable_mp(this, &ConnectDialog::_text_submitted)); - vbc_left->add_margin_child(TTR("Receiver Method:"), dst_method); + hbc_method->add_child(dst_method); + + open_method_tree = memnew(Button); + hbc_method->add_child(open_method_tree); + open_method_tree->set_text("Pick"); + open_method_tree->connect("pressed", callable_mp(this, &ConnectDialog::_open_method_popup)); - advanced = memnew(CheckButton); + advanced = memnew(CheckButton(TTR("Advanced"))); vbc_left->add_child(advanced); - advanced->set_text(TTR("Advanced")); advanced->set_h_size_flags(Control::SIZE_SHRINK_BEGIN | Control::SIZE_EXPAND); advanced->set_pressed(EditorSettings::get_singleton()->get_project_metadata("editor_metadata", "use_advanced_connections", false)); advanced->connect("pressed", callable_mp(this, &ConnectDialog::_advanced_pressed)); @@ -566,7 +751,7 @@ ConnectDialog::~ConnectDialog() { // Originally copied and adapted from EditorProperty, try to keep style in sync. Control *ConnectionsDockTree::make_custom_tooltip(const String &p_text) const { EditorHelpBit *help_bit = memnew(EditorHelpBit); - help_bit->get_rich_text()->set_fixed_size_to_width(360 * EDSCALE); + help_bit->get_rich_text()->set_custom_minimum_size(Size2(360 * EDSCALE, 1)); // p_text is expected to be something like this: // "gui_input::(event: InputEvent)::<Signal description>" @@ -605,9 +790,6 @@ void ConnectionsDock::_filter_changed(const String &p_text) { * Creates or edits connections based on state of the ConnectDialog when "Connect" is pressed. */ void ConnectionsDock::_make_or_edit_connection() { - TreeItem *it = tree->get_selected(); - ERR_FAIL_COND(!it); - NodePath dst_path = connect_dialog->get_dst_path(); Node *target = selected_node->get_node(dst_path); ERR_FAIL_COND(!target); @@ -645,27 +827,21 @@ void ConnectionsDock::_make_or_edit_connection() { add_script_function = !found_inherited_function; } - PackedStringArray script_function_args; - if (add_script_function) { - // Pick up args here before "it" is deleted by update_tree. - script_function_args = it->get_metadata(0).operator Dictionary()["args"]; - script_function_args.resize(script_function_args.size() - cd.unbinds); - for (int i = 0; i < cd.binds.size(); i++) { - script_function_args.push_back("extra_arg_" + itos(i) + ":" + Variant::get_type_name(cd.binds[i].get_type())); - } - } if (connect_dialog->is_editing()) { - _disconnect(*it); + _disconnect(connect_dialog->get_source_connection_data()); _connect(cd); } else { _connect(cd); } - // IMPORTANT NOTE: _disconnect and _connect cause an update_tree, which will delete the object "it" is pointing to. - it = nullptr; - if (add_script_function) { + PackedStringArray script_function_args = connect_dialog->get_signal_args(); + script_function_args.resize(script_function_args.size() - cd.unbinds); + for (int i = 0; i < cd.binds.size(); i++) { + script_function_args.push_back("extra_arg_" + itos(i) + ":" + Variant::get_type_name(cd.binds[i].get_type())); + } + EditorNode::get_singleton()->emit_signal(SNAME("script_add_function_request"), target, cd.method, script_function_args); hide(); } @@ -676,7 +852,7 @@ void ConnectionsDock::_make_or_edit_connection() { /* * Creates single connection w/ undo-redo functionality. */ -void ConnectionsDock::_connect(ConnectDialog::ConnectionData p_cd) { +void ConnectionsDock::_connect(const ConnectDialog::ConnectionData &p_cd) { Node *source = Object::cast_to<Node>(p_cd.source); Node *target = Object::cast_to<Node>(p_cd.target); @@ -700,18 +876,15 @@ void ConnectionsDock::_connect(ConnectDialog::ConnectionData p_cd) { /* * Break single connection w/ undo-redo functionality. */ -void ConnectionsDock::_disconnect(TreeItem &p_item) { - Connection connection = p_item.get_metadata(0); - ConnectDialog::ConnectionData cd = connection; - - ERR_FAIL_COND(cd.source != selected_node); // Shouldn't happen but... Bugcheck. +void ConnectionsDock::_disconnect(const ConnectDialog::ConnectionData &p_cd) { + ERR_FAIL_COND(p_cd.source != selected_node); // Shouldn't happen but... Bugcheck. EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); - undo_redo->create_action(vformat(TTR("Disconnect '%s' from '%s'"), cd.signal, cd.method)); + undo_redo->create_action(vformat(TTR("Disconnect '%s' from '%s'"), p_cd.signal, p_cd.method)); - Callable callable = cd.get_callable(); - undo_redo->add_do_method(selected_node, "disconnect", cd.signal, callable); - undo_redo->add_undo_method(selected_node, "connect", cd.signal, callable, cd.binds, cd.flags); + Callable callable = p_cd.get_callable(); + undo_redo->add_do_method(selected_node, "disconnect", p_cd.signal, callable); + undo_redo->add_undo_method(selected_node, "connect", p_cd.signal, callable, p_cd.flags); undo_redo->add_do_method(this, "update_tree"); undo_redo->add_undo_method(this, "update_tree"); undo_redo->add_do_method(SceneTreeDock::get_singleton()->get_tree_editor(), "update_tree"); // To force redraw of scene tree. @@ -741,7 +914,7 @@ void ConnectionsDock::_disconnect_all() { if (!_is_connection_inherited(connection)) { ConnectDialog::ConnectionData cd = connection; undo_redo->add_do_method(selected_node, "disconnect", cd.signal, cd.get_callable()); - undo_redo->add_undo_method(selected_node, "connect", cd.signal, cd.get_callable(), cd.binds, cd.flags); + undo_redo->add_undo_method(selected_node, "connect", cd.signal, cd.get_callable(), cd.flags); } child = child->get_next(); } @@ -795,7 +968,10 @@ bool ConnectionsDock::_is_connection_inherited(Connection &p_connection) { * Open connection dialog with TreeItem data to CREATE a brand-new connection. */ void ConnectionsDock::_open_connection_dialog(TreeItem &p_item) { - String signal_name = p_item.get_metadata(0).operator Dictionary()["name"]; + Dictionary sinfo = p_item.get_metadata(0); + String signal_name = sinfo["name"]; + PackedStringArray signal_args = sinfo["args"]; + const String &signal_name_ref = signal_name; Node *dst_node = selected_node->get_owner() ? selected_node->get_owner() : selected_node; @@ -809,22 +985,30 @@ void ConnectionsDock::_open_connection_dialog(TreeItem &p_item) { cd.target = dst_node; cd.method = ConnectDialog::generate_method_callback_name(cd.source, signal_name, cd.target); connect_dialog->popup_dialog(signal_name_ref); - connect_dialog->init(cd); + connect_dialog->init(cd, signal_args); connect_dialog->set_title(TTR("Connect a Signal to a Method")); } /* * Open connection dialog with Connection data to EDIT an existing connection. */ -void ConnectionsDock::_open_connection_dialog(ConnectDialog::ConnectionData p_cd) { - Node *src = Object::cast_to<Node>(p_cd.source); - Node *dst = Object::cast_to<Node>(p_cd.target); +void ConnectionsDock::_open_edit_connection_dialog(TreeItem &p_item) { + TreeItem *signal_item = p_item.get_parent(); + ERR_FAIL_COND(!signal_item); + + Connection connection = p_item.get_metadata(0); + ConnectDialog::ConnectionData cd = connection; + + Node *src = Object::cast_to<Node>(cd.source); + Node *dst = Object::cast_to<Node>(cd.target); if (src && dst) { - const String &signal_name_ref = p_cd.signal; - connect_dialog->set_title(TTR("Edit Connection:") + p_cd.signal); + const String &signal_name_ref = cd.signal; + PackedStringArray signal_args = signal_item->get_metadata(0).operator Dictionary()["args"]; + + connect_dialog->set_title(vformat(TTR("Edit Connection: '%s'"), cd.signal)); connect_dialog->popup_dialog(signal_name_ref); - connect_dialog->init(p_cd, true); + connect_dialog->init(cd, signal_args, true); } } @@ -899,14 +1083,14 @@ void ConnectionsDock::_handle_slot_menu_option(int p_option) { switch (p_option) { case EDIT: { - Connection connection = item->get_metadata(0); - _open_connection_dialog(connection); + _open_edit_connection_dialog(*item); } break; case GO_TO_SCRIPT: { _go_to_script(*item); } break; case DISCONNECT: { - _disconnect(*item); + Connection connection = item->get_metadata(0); + _disconnect(connection); update_tree(); } break; } @@ -957,7 +1141,8 @@ void ConnectionsDock::_connect_pressed() { if (_is_item_signal(*item)) { _open_connection_dialog(*item); } else { - _disconnect(*item); + Connection connection = item->get_metadata(0); + _disconnect(connection); update_tree(); } } @@ -985,6 +1170,10 @@ void ConnectionsDock::set_node(Node *p_node) { } void ConnectionsDock::update_tree() { + String prev_selected; + if (tree->is_anything_selected()) { + prev_selected = tree->get_selected()->get_text(0); + } tree->clear(); if (!selected_node) { @@ -1076,7 +1265,14 @@ void ConnectionsDock::update_tree() { // Create the children of the subsection - the actual list of signals. TreeItem *signal_item = tree->create_item(section_item); - signal_item->set_text(0, String(signal_name) + signaldesc); + String signame = String(signal_name) + signaldesc; + signal_item->set_text(0, signame); + + if (signame == prev_selected) { + signal_item->select(0); + prev_selected = ""; + } + Dictionary sinfo; sinfo["name"] = signal_name; sinfo["args"] = argnames; @@ -1238,9 +1434,6 @@ ConnectionsDock::ConnectionsDock() { tree->connect("item_mouse_selected", callable_mp(this, &ConnectionsDock::_rmb_pressed)); add_theme_constant_override("separation", 3 * EDSCALE); - - EDITOR_DEF("interface/editors/default_signal_callback_name", "_on_{node_name}_{signal_name}"); - EDITOR_DEF("interface/editors/default_signal_callback_to_self_name", "_on_{signal_name}"); } ConnectionsDock::~ConnectionsDock() { |