diff options
Diffstat (limited to 'editor/connections_dialog.cpp')
-rw-r--r-- | editor/connections_dialog.cpp | 303 |
1 files changed, 215 insertions, 88 deletions
diff --git a/editor/connections_dialog.cpp b/editor/connections_dialog.cpp index 7a5ebc5c00..c5b81c4685 100644 --- a/editor/connections_dialog.cpp +++ b/editor/connections_dialog.cpp @@ -37,6 +37,25 @@ #include "scene/gui/label.h" #include "scene/gui/popup_menu.h" +static Node *_find_first_script(Node *p_root, Node *p_node) { + if (p_node != p_root && p_node->get_owner() != p_root) { + return NULL; + } + if (!p_node->get_script().is_null()) { + return p_node; + } + + for (int i = 0; i < p_node->get_child_count(); i++) { + + Node *ret = _find_first_script(p_root, p_node->get_child(i)); + if (ret) { + return ret; + } + } + + return NULL; +} + class ConnectDialogBinds : public Object { GDCLASS(ConnectDialogBinds, Object); @@ -94,20 +113,19 @@ Signal automatically called by parent dialog. void ConnectDialog::ok_pressed() { if (dst_method->get_text() == "") { - error->set_text(TTR("Method in target Node must be specified!")); + error->set_text(TTR("Method in target node must be specified.")); error->popup_centered_minsize(); return; } Node *target = tree->get_selected(); if (target->get_script().is_null()) { if (!target->has_method(dst_method->get_text())) { - error->set_text(TTR("Target method not found! Specify a valid method or attach a script to target Node.")); + error->set_text(TTR("Target method not found. Specify a valid method or attach a script to the target node.")); error->popup_centered_minsize(); return; } } emit_signal("connected"); - hide(); } void ConnectDialog::_cancel_pressed() { @@ -122,17 +140,11 @@ void ConnectDialog::_tree_node_selected() { Node *current = tree->get_selected(); - if (!current) { - make_callback->hide(); + if (!current) return; - } - - if (current->get_script().is_null()) - make_callback->hide(); - else - make_callback->show(); - dst_path->set_text(source->get_path_to(current)); + dst_path = source->get_path_to(current); + get_ok()->set_disabled(false); } /* @@ -160,7 +172,9 @@ void ConnectDialog::_add_bind() { case Variant::BASIS: value = Basis(); break; case Variant::TRANSFORM: value = Transform(); break; case Variant::COLOR: value = Color(); break; - default: { ERR_FAIL(); } break; + default: { + ERR_FAIL(); + } break; } ERR_FAIL_COND(value.get_type() == Variant::NIL); @@ -193,6 +207,7 @@ void ConnectDialog::_notification(int p_what) { void ConnectDialog::_bind_methods() { + ClassDB::bind_method("_advanced_pressed", &ConnectDialog::_advanced_pressed); ClassDB::bind_method("_cancel", &ConnectDialog::_cancel_pressed); ClassDB::bind_method("_tree_node_selected", &ConnectDialog::_tree_node_selected); ClassDB::bind_method("_add_bind", &ConnectDialog::_add_bind); @@ -213,7 +228,7 @@ StringName ConnectDialog::get_signal_name() const { NodePath ConnectDialog::get_dst_path() const { - return dst_path->get_text(); + return dst_path; } void ConnectDialog::set_dst_node(Node *p_node) { @@ -270,8 +285,13 @@ void ConnectDialog::init(Connection c, bool bEdit) { tree->set_selected(NULL); tree->set_marked(source, true); - set_dst_node(static_cast<Node *>(c.target)); - set_dst_method(c.method); + if (c.target) { + get_ok()->set_disabled(false); + set_dst_node(static_cast<Node *>(c.target)); + set_dst_method(c.method); + } else { + get_ok()->set_disabled(true); + } bool bDeferred = (c.flags & CONNECT_DEFERRED) == CONNECT_DEFERRED; bool bOneshot = (c.flags & CONNECT_ONESHOT) == CONNECT_ONESHOT; @@ -286,8 +306,42 @@ void ConnectDialog::init(Connection c, bool bEdit) { bEditMode = bEdit; } +void ConnectDialog::popup_dialog(const String &p_for_signal) { + + from_signal->set_text(p_for_signal); + error_label->add_color_override("font_color", get_color("error_color", "Editor")); + if (!advanced->is_pressed()) + error_label->set_visible(!_find_first_script(get_tree()->get_edited_scene_root(), get_tree()->get_edited_scene_root())); + + popup_centered(); +} + +void ConnectDialog::_advanced_pressed() { + + if (advanced->is_pressed()) { + set_custom_minimum_size(Size2(900, 500) * EDSCALE); + connect_to_label->set_text(TTR("Connect to Node:")); + tree->set_connect_to_script_mode(false); + + vbc_right->show(); + error_label->hide(); + } else { + set_custom_minimum_size(Size2(600, 500) * EDSCALE); + set_size(Size2()); + connect_to_label->set_text(TTR("Connect to Script:")); + tree->set_connect_to_script_mode(true); + + vbc_right->hide(); + error_label->set_visible(!_find_first_script(get_tree()->get_edited_scene_root(), get_tree()->get_edited_scene_root())); + } + + set_position((get_viewport_rect().size - get_custom_minimum_size()) / 2); +} + ConnectDialog::ConnectDialog() { + set_custom_minimum_size(Size2(600, 500) * EDSCALE); + VBoxContainer *vbc = memnew(VBoxContainer); add_child(vbc); @@ -299,15 +353,28 @@ ConnectDialog::ConnectDialog() { main_hb->add_child(vbc_left); vbc_left->set_h_size_flags(SIZE_EXPAND_FILL); + from_signal = memnew(LineEdit); + from_signal->set_editable(false); + vbc_left->add_margin_child(TTR("From Signal:"), from_signal); + tree = memnew(SceneTreeEditor(false)); + tree->set_connecting_signal(true); tree->get_scene_tree()->connect("item_activated", this, "_ok"); tree->connect("node_selected", this, "_tree_node_selected"); + tree->set_connect_to_script_mode(true); + + Node *mc = vbc_left->add_margin_child(TTR("Connect to Script:"), tree, true); + connect_to_label = Object::cast_to<Label>(vbc_left->get_child(mc->get_index() - 1)); - vbc_left->add_margin_child(TTR("Connect To Node:"), tree, true); + error_label = memnew(Label); + error_label->set_text(TTR("Scene does not contain any script.")); + vbc_left->add_child(error_label); + error_label->hide(); - VBoxContainer *vbc_right = memnew(VBoxContainer); + vbc_right = memnew(VBoxContainer); main_hb->add_child(vbc_right); vbc_right->set_h_size_flags(SIZE_EXPAND_FILL); + vbc_right->hide(); HBoxContainer *add_bind_hb = memnew(HBoxContainer); @@ -317,7 +384,7 @@ ConnectDialog::ConnectDialog() { type_list->add_item("bool", Variant::BOOL); type_list->add_item("int", Variant::INT); type_list->add_item("real", Variant::REAL); - type_list->add_item("string", Variant::STRING); + type_list->add_item("String", Variant::STRING); type_list->add_item("Vector2", Variant::VECTOR2); type_list->add_item("Rect2", Variant::RECT2); type_list->add_item("Vector3", Variant::VECTOR3); @@ -345,47 +412,42 @@ ConnectDialog::ConnectDialog() { vbc_right->add_margin_child(TTR("Extra Call Arguments:"), bind_editor, true); - dst_path = memnew(LineEdit); - vbc->add_margin_child(TTR("Path to Node:"), dst_path); - HBoxContainer *dstm_hb = memnew(HBoxContainer); - vbc->add_margin_child("Method In Node:", dstm_hb); + vbc_left->add_margin_child("Receiver Method:", dstm_hb); dst_method = memnew(LineEdit); dst_method->set_h_size_flags(SIZE_EXPAND_FILL); dstm_hb->add_child(dst_method); - /* - dst_method_list = memnew( MenuButton ); - dst_method_list->set_text("List..."); - dst_method_list->set_anchor( MARGIN_RIGHT, ANCHOR_END ); - dst_method_list->set_anchor( MARGIN_LEFT, ANCHOR_END ); - dst_method_list->set_anchor( MARGIN_TOP, ANCHOR_END ); - dst_method_list->set_anchor( MARGIN_BOTTOM, ANCHOR_END ); - dst_method_list->set_begin( Point2( 70,59) ); - dst_method_list->set_end( Point2( 15,39 ) ); - */ - - make_callback = memnew(CheckButton); - make_callback->set_toggle_mode(true); - make_callback->set_pressed(EDITOR_DEF("text_editor/tools/create_signal_callbacks", true)); - make_callback->set_text(TTR("Make Function")); - dstm_hb->add_child(make_callback); - - deferred = memnew(CheckButton); + advanced = memnew(CheckButton); + dstm_hb->add_child(advanced); + advanced->set_text(TTR("Advanced")); + advanced->connect("pressed", this, "_advanced_pressed"); + + // Add spacing so the tree and inspector are the same size. + Control *spacing = memnew(Control); + spacing->set_custom_minimum_size(Size2(0, 4) * EDSCALE); + vbc_right->add_child(spacing); + + deferred = memnew(CheckBox); + deferred->set_h_size_flags(0); deferred->set_text(TTR("Deferred")); - dstm_hb->add_child(deferred); + deferred->set_tooltip(TTR("Defers the signal, storing it in a queue and only firing it at idle time.")); + vbc_right->add_child(deferred); - oneshot = memnew(CheckButton); + oneshot = memnew(CheckBox); + oneshot->set_h_size_flags(0); oneshot->set_text(TTR("Oneshot")); - dstm_hb->add_child(oneshot); + oneshot->set_tooltip(TTR("Disconnects the signal after its first emission.")); + vbc_right->add_child(oneshot); set_as_toplevel(true); cdbinds = memnew(ConnectDialogBinds); - error = memnew(ConfirmationDialog); + error = memnew(AcceptDialog); add_child(error); + error->set_title(TTR("Cannot connect signal")); error->get_ok()->set_text(TTR("Close")); get_ok()->set_text(TTR("Connect")); } @@ -395,7 +457,22 @@ ConnectDialog::~ConnectDialog() { memdelete(cdbinds); } -//ConnectionsDock ========================== +////////////////////////////////////////// + +// 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->add_style_override("panel", get_stylebox("panel", "TooltipPanel")); + help_bit->get_rich_text()->set_fixed_size_to_width(360 * EDSCALE); + + String text = TTR("Signal:") + " [u][b]" + p_text.get_slice("::", 0) + "[/b][/u]"; + text += p_text.get_slice("::", 1).strip_edges() + "\n"; + text += p_text.get_slice("::", 2).strip_edges(); + help_bit->set_text(text); + help_bit->call_deferred("set_text", text); //hack so it uses proper theme once inside scene + return help_bit; +} struct _ConnectionsDockMethodInfoSort { @@ -427,10 +504,29 @@ void ConnectionsDock::_make_or_edit_connection() { bool oshot = connect_dialog->get_oneshot(); cToMake.flags = CONNECT_PERSIST | (defer ? CONNECT_DEFERRED : 0) | (oshot ? CONNECT_ONESHOT : 0); - bool add_script_function = connect_dialog->get_make_callback(); + // Conditions to add function: must have a script and must not have the method already + // (in the class, the script itself, or inherited). + bool add_script_function = false; + Ref<Script> script = target->get_script(); + if (!target->get_script().is_null() && !ClassDB::has_method(target->get_class(), cToMake.method)) { + // There is a chance that the method is inherited from another script. + bool found_inherited_function = false; + Ref<Script> inherited_script = script->get_base_script(); + while (!inherited_script.is_null()) { + int line = inherited_script->get_language()->find_function(cToMake.method, inherited_script->get_source_code()); + if (line != -1) { + found_inherited_function = true; + break; + } + + inherited_script = inherited_script->get_base_script(); + } + + add_script_function = !found_inherited_function; + } PoolStringArray script_function_args; if (add_script_function) { - // pick up args here before "it" is deleted by update_tree + // Pick up args here before "it" is deleted by update_tree. script_function_args = it->get_metadata(0).operator Dictionary()["args"]; for (int i = 0; i < cToMake.binds.size(); i++) { script_function_args.append("extra_arg_" + itos(i)); @@ -444,8 +540,7 @@ void ConnectionsDock::_make_or_edit_connection() { _connect(cToMake); } - // IMPORTANT NOTE: _disconnect and _connect cause an update_tree, - // which will delete the object "it" is pointing to + // IMPORTANT NOTE: _disconnect and _connect cause an update_tree, which will delete the object "it" is pointing to. it = NULL; if (add_script_function) { @@ -485,7 +580,7 @@ Break single connection w/ undo-redo functionality. void ConnectionsDock::_disconnect(TreeItem &item) { Connection c = item.get_metadata(0); - ERR_FAIL_COND(c.source != selectedNode); //shouldn't happen but...bugcheck + ERR_FAIL_COND(c.source != selectedNode); // Shouldn't happen but... Bugcheck. undo_redo->create_action(vformat(TTR("Disconnect '%s' from '%s'"), c.signal, c.method)); @@ -493,7 +588,7 @@ void ConnectionsDock::_disconnect(TreeItem &item) { undo_redo->add_undo_method(selectedNode, "connect", c.signal, c.target, c.method, c.binds, c.flags); undo_redo->add_do_method(this, "update_tree"); undo_redo->add_undo_method(this, "update_tree"); - undo_redo->add_do_method(EditorNode::get_singleton()->get_scene_tree_dock()->get_tree_editor(), "update_tree"); //to force redraw of scene tree + undo_redo->add_do_method(EditorNode::get_singleton()->get_scene_tree_dock()->get_tree_editor(), "update_tree"); // To force redraw of scene tree. undo_redo->add_undo_method(EditorNode::get_singleton()->get_scene_tree_dock()->get_tree_editor(), "update_tree"); undo_redo->commit_action(); @@ -532,7 +627,7 @@ void ConnectionsDock::_disconnect_all() { void ConnectionsDock::_tree_item_selected() { TreeItem *item = tree->get_selected(); - if (!item) { //Unlikely. Disable button just in case. + if (!item) { // Unlikely. Disable button just in case. connect_button->set_text(TTR("Connect...")); connect_button->set_disabled(true); } else if (_is_item_signal(*item)) { @@ -544,7 +639,7 @@ void ConnectionsDock::_tree_item_selected() { } } -void ConnectionsDock::_tree_item_activated() { //"Activation" on double-click. +void ConnectionsDock::_tree_item_activated() { // "Activation" on double-click. TreeItem *item = tree->get_selected(); @@ -569,16 +664,16 @@ Open connection dialog with TreeItem data to CREATE a brand-new connection. void ConnectionsDock::_open_connection_dialog(TreeItem &item) { String signal = item.get_metadata(0).operator Dictionary()["name"]; - String signalname = signal; + const String &signalname = signal; String midname = selectedNode->get_name(); for (int i = 0; i < midname.length(); i++) { //TODO: Regex filter may be cleaner. CharType c = midname[i]; if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_')) { if (c == ' ') { - //Replace spaces with underlines. + // Replace spaces with underlines. c = '_'; } else { - //Remove any other characters. + // Remove any other characters. midname.remove(i); i--; continue; @@ -588,6 +683,10 @@ void ConnectionsDock::_open_connection_dialog(TreeItem &item) { } Node *dst_node = selectedNode->get_owner() ? selectedNode->get_owner() : selectedNode; + if (!dst_node || dst_node->get_script().is_null()) { + dst_node = _find_first_script(get_tree()->get_edited_scene_root(), get_tree()->get_edited_scene_root()); + } + StringName dst_method = "_on_" + midname + "_" + signal; Connection c; @@ -595,10 +694,9 @@ void ConnectionsDock::_open_connection_dialog(TreeItem &item) { c.signal = StringName(signalname); c.target = dst_node; c.method = dst_method; - + connect_dialog->popup_dialog(signalname); connect_dialog->init(c); - connect_dialog->set_title(TTR("Connect Signal: ") + signalname); - connect_dialog->popup_centered_ratio(); + connect_dialog->set_title(TTR("Connect a Signal to a Method")); } /* @@ -610,9 +708,9 @@ void ConnectionsDock::_open_connection_dialog(Connection cToEdit) { Node *dst = static_cast<Node *>(cToEdit.target); if (src && dst) { + connect_dialog->set_title(TTR("Edit Connection:") + cToEdit.signal); + connect_dialog->popup_centered(); connect_dialog->init(cToEdit, true); - connect_dialog->set_title(TTR("Edit Connection: ") + cToEdit.signal); - connect_dialog->popup_centered_ratio(); } } @@ -760,13 +858,12 @@ void ConnectionsDock::update_tree() { selectedNode->get_signal_list(&node_signals); - //node_signals.sort_custom<_ConnectionsDockMethodInfoSort>(); bool did_script = false; StringName base = selectedNode->get_class(); while (base) { - List<MethodInfo> node_signals; + List<MethodInfo> node_signals2; Ref<Texture> icon; String name; @@ -774,7 +871,7 @@ void ConnectionsDock::update_tree() { Ref<Script> scr = selectedNode->get_script(); if (scr.is_valid()) { - scr->get_script_signal_list(&node_signals); + scr->get_script_signal_list(&node_signals2); if (scr->get_path().is_resource_file()) name = scr->get_path().get_file(); else @@ -784,34 +881,37 @@ void ConnectionsDock::update_tree() { icon = get_icon(scr->get_class(), "EditorIcons"); } } - } else { - ClassDB::get_signal_list(base, &node_signals, true); + ClassDB::get_signal_list(base, &node_signals2, true); if (has_icon(base, "EditorIcons")) { icon = get_icon(base, "EditorIcons"); } name = base; } + if (!icon.is_valid()) { + icon = get_icon("Object", "EditorIcons"); + } + TreeItem *pitem = NULL; - if (node_signals.size()) { + if (node_signals2.size()) { pitem = tree->create_item(root); pitem->set_text(0, name); pitem->set_icon(0, icon); pitem->set_selectable(0, false); pitem->set_editable(0, false); pitem->set_custom_bg_color(0, get_color("prop_subsection", "Editor")); - node_signals.sort(); + node_signals2.sort(); } - for (List<MethodInfo>::Element *E = node_signals.front(); E; E = E->next()) { + for (List<MethodInfo>::Element *E = node_signals2.front(); E; E = E->next()) { MethodInfo &mi = E->get(); - String signaldesc; - signaldesc = mi.name + "("; + StringName signal_name = mi.name; + String signaldesc = "("; PoolStringArray argnames; if (mi.arguments.size()) { signaldesc += " "; @@ -832,19 +932,56 @@ void ConnectionsDock::update_tree() { } signaldesc += " "; } - signaldesc += ")"; TreeItem *item = tree->create_item(pitem); - item->set_text(0, signaldesc); + item->set_text(0, String(signal_name) + signaldesc); Dictionary sinfo; - sinfo["name"] = mi.name; + sinfo["name"] = signal_name; sinfo["args"] = argnames; item->set_metadata(0, sinfo); item->set_icon(0, get_icon("Signal", "EditorIcons")); + // Set tooltip with the signal's documentation. + { + String descr; + bool found = false; + + Map<StringName, Map<StringName, String> >::Element *G = descr_cache.find(base); + if (G) { + Map<StringName, String>::Element *F = G->get().find(signal_name); + if (F) { + found = true; + descr = F->get(); + } + } + + if (!found) { + DocData *dd = EditorHelp::get_doc_data(); + Map<String, DocData::ClassDoc>::Element *F = dd->class_list.find(base); + while (F && descr == String()) { + for (int i = 0; i < F->get().signals.size(); i++) { + if (F->get().signals[i].name == signal_name.operator String()) { + descr = F->get().signals[i].description.strip_edges(); + break; + } + } + if (!F->get().inherits.empty()) { + F = dd->class_list.find(F->get().inherits); + } else { + break; + } + } + descr_cache[base][signal_name] = descr; + } + + // "::" separators used in make_custom_tooltip for formatting. + item->set_tooltip(0, String(signal_name) + "::" + signaldesc + "::" + descr); + } + + // List existing connections List<Object::Connection> connections; - selectedNode->get_signal_connection_list(mi.name, &connections); + selectedNode->get_signal_connection_list(signal_name, &connections); for (List<Object::Connection>::Element *F = connections.front(); F; F = F->next()) { @@ -887,7 +1024,7 @@ void ConnectionsDock::update_tree() { } } - connect_button->set_text(TTR("Connect")); + connect_button->set_text(TTR("Connect...")); connect_button->set_disabled(true); } @@ -898,7 +1035,7 @@ ConnectionsDock::ConnectionsDock(EditorNode *p_editor) { VBoxContainer *vbc = this; - tree = memnew(Tree); + tree = memnew(ConnectionsDockTree); tree->set_columns(1); tree->set_select_mode(Tree::SELECT_ROW); tree->set_hide_root(true); @@ -907,7 +1044,6 @@ ConnectionsDock::ConnectionsDock(EditorNode *p_editor) { tree->set_allow_rmb_select(true); connect_button = memnew(Button); - connect_button->set_text(TTR("Connect")); HBoxContainer *hb = memnew(HBoxContainer); vbc->add_child(hb); hb->add_spacer(); @@ -937,15 +1073,6 @@ ConnectionsDock::ConnectionsDock(EditorNode *p_editor) { slot_menu->add_item(TTR("Go To Method"), GO_TO_SCRIPT); slot_menu->add_item(TTR("Disconnect"), DISCONNECT); - /* - node_only->set_anchor( MARGIN_TOP, ANCHOR_END ); - node_only->set_anchor( MARGIN_BOTTOM, ANCHOR_END ); - node_only->set_anchor( MARGIN_RIGHT, ANCHOR_END ); - - node_only->set_begin( Point2( 20,51) ); - node_only->set_end( Point2( 10,44) ); - */ - connect_dialog->connect("connected", this, "_make_or_edit_connection"); tree->connect("item_selected", this, "_tree_item_selected"); tree->connect("item_activated", this, "_tree_item_activated"); |