summaryrefslogtreecommitdiff
path: root/editor/connections_dialog.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'editor/connections_dialog.cpp')
-rw-r--r--editor/connections_dialog.cpp303
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");