godot/editor/connections_dialog.cpp
DualMatrix 1775c796a4 Automatically add extra args to func when creating it from create signal window
Automatically add extra arguments to function when creating it from create signal window. They will be called extra_arg_1, extra_arg_2,...
2018-09-16 21:01:17 +02:00

959 lines
27 KiB
C++

/*************************************************************************/
/* connections_dialog.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 "connections_dialog.h"
#include "core/print_string.h"
#include "editor_node.h"
#include "editor_settings.h"
#include "plugins/script_editor_plugin.h"
#include "scene/gui/label.h"
#include "scene/gui/popup_menu.h"
class ConnectDialogBinds : public Object {
GDCLASS(ConnectDialogBinds, Object);
public:
Vector<Variant> params;
bool _set(const StringName &p_name, const Variant &p_value) {
String name = p_name;
if (name.begins_with("bind/")) {
int which = name.get_slice("/", 1).to_int() - 1;
ERR_FAIL_INDEX_V(which, params.size(), false);
params.write[which] = p_value;
} else
return false;
return true;
}
bool _get(const StringName &p_name, Variant &r_ret) const {
String name = p_name;
if (name.begins_with("bind/")) {
int which = name.get_slice("/", 1).to_int() - 1;
ERR_FAIL_INDEX_V(which, params.size(), false);
r_ret = params[which];
} else
return false;
return true;
}
void _get_property_list(List<PropertyInfo> *p_list) const {
for (int i = 0; i < params.size(); i++) {
p_list->push_back(PropertyInfo(params[i].get_type(), "bind/" + itos(i + 1)));
}
}
void notify_changed() {
_change_notify();
}
ConnectDialogBinds() {
}
};
/*
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->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->popup_centered_minsize();
return;
}
}
emit_signal("connected");
hide();
}
void ConnectDialog::_cancel_pressed() {
hide();
}
/*
Called each time a target node is selected within the target node tree.
*/
void ConnectDialog::_tree_node_selected() {
Node *current = tree->get_selected();
if (!current) {
make_callback->hide();
return;
}
if (current->get_script().is_null())
make_callback->hide();
else
make_callback->show();
dst_path->set_text(source->get_path_to(current));
}
/*
Adds a new parameter bind to connection.
*/
void ConnectDialog::_add_bind() {
if (cdbinds->params.size() >= VARIANT_ARG_MAX)
return;
Variant::Type vt = (Variant::Type)type_list->get_item_id(type_list->get_selected());
Variant value;
switch (vt) {
case Variant::BOOL: value = false; break;
case Variant::INT: value = 0; break;
case Variant::REAL: value = 0.0; break;
case Variant::STRING: value = ""; break;
case Variant::VECTOR2: value = Vector2(); break;
case Variant::RECT2: value = Rect2(); break;
case Variant::VECTOR3: value = Vector3(); break;
case Variant::PLANE: value = Plane(); break;
case Variant::QUAT: value = Quat(); break;
case Variant::AABB: value = AABB(); break;
case Variant::BASIS: value = Basis(); break;
case Variant::TRANSFORM: value = Transform(); break;
case Variant::COLOR: value = Color(); break;
default: { ERR_FAIL(); } break;
}
ERR_FAIL_COND(value.get_type() == Variant::NIL);
cdbinds->params.push_back(value);
cdbinds->notify_changed();
}
/*
Remove parameter bind from connection.
*/
void ConnectDialog::_remove_bind() {
String st = bind_editor->get_selected_path();
if (st == "")
return;
int idx = st.get_slice("/", 1).to_int() - 1;
ERR_FAIL_INDEX(idx, cdbinds->params.size());
cdbinds->params.remove(idx);
cdbinds->notify_changed();
}
void ConnectDialog::_notification(int p_what) {
if (p_what == NOTIFICATION_ENTER_TREE) {
bind_editor->edit(cdbinds);
}
}
void ConnectDialog::_bind_methods() {
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);
ClassDB::bind_method("_remove_bind", &ConnectDialog::_remove_bind);
ADD_SIGNAL(MethodInfo("connected"));
}
Node *ConnectDialog::get_source() const {
return source;
}
StringName ConnectDialog::get_signal_name() const {
return signal;
}
NodePath ConnectDialog::get_dst_path() const {
return dst_path->get_text();
}
void ConnectDialog::set_dst_node(Node *p_node) {
tree->set_selected(p_node);
}
StringName ConnectDialog::get_dst_method_name() const {
String txt = dst_method->get_text();
if (txt.find("(") != -1)
txt = txt.left(txt.find("(")).strip_edges();
return txt;
}
void ConnectDialog::set_dst_method(const StringName &p_method) {
dst_method->set_text(p_method);
}
Vector<Variant> ConnectDialog::get_binds() const {
return cdbinds->params;
}
bool ConnectDialog::get_deferred() const {
return deferred->is_pressed();
}
bool ConnectDialog::get_oneshot() const {
return oneshot->is_pressed();
}
/*
Returns true if ConnectDialog is being used to edit an existing connection.
*/
bool ConnectDialog::is_editing() const {
return bEditMode;
}
/*
Initialize ConnectDialog and populate fields with expected data.
If creating a connection from scratch, sensible defaults are used.
If editing an existing connection, previous data is retained.
*/
void ConnectDialog::init(Connection c, bool bEdit) {
source = static_cast<Node *>(c.source);
signal = c.signal;
tree->set_selected(NULL);
tree->set_marked(source, true);
set_dst_node(static_cast<Node *>(c.target));
set_dst_method(c.method);
bool bDeferred = (c.flags & CONNECT_DEFERRED) == CONNECT_DEFERRED;
bool bOneshot = (c.flags & CONNECT_ONESHOT) == CONNECT_ONESHOT;
deferred->set_pressed(bDeferred);
oneshot->set_pressed(bOneshot);
cdbinds->params.clear();
cdbinds->params = c.binds;
cdbinds->notify_changed();
bEditMode = bEdit;
}
ConnectDialog::ConnectDialog() {
VBoxContainer *vbc = memnew(VBoxContainer);
add_child(vbc);
HBoxContainer *main_hb = memnew(HBoxContainer);
vbc->add_child(main_hb);
main_hb->set_v_size_flags(SIZE_EXPAND_FILL);
VBoxContainer *vbc_left = memnew(VBoxContainer);
main_hb->add_child(vbc_left);
vbc_left->set_h_size_flags(SIZE_EXPAND_FILL);
tree = memnew(SceneTreeEditor(false));
tree->get_scene_tree()->connect("item_activated", this, "_ok");
tree->connect("node_selected", this, "_tree_node_selected");
vbc_left->add_margin_child(TTR("Connect To Node:"), tree, true);
VBoxContainer *vbc_right = memnew(VBoxContainer);
main_hb->add_child(vbc_right);
vbc_right->set_h_size_flags(SIZE_EXPAND_FILL);
HBoxContainer *add_bind_hb = memnew(HBoxContainer);
type_list = memnew(OptionButton);
type_list->set_h_size_flags(SIZE_EXPAND_FILL);
add_bind_hb->add_child(type_list);
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("Vector2", Variant::VECTOR2);
type_list->add_item("Rect2", Variant::RECT2);
type_list->add_item("Vector3", Variant::VECTOR3);
type_list->add_item("Plane", Variant::PLANE);
type_list->add_item("Quat", Variant::QUAT);
type_list->add_item("AABB", Variant::AABB);
type_list->add_item("Basis", Variant::BASIS);
type_list->add_item("Transform", Variant::TRANSFORM);
type_list->add_item("Color", Variant::COLOR);
type_list->select(0);
Button *add_bind = memnew(Button);
add_bind->set_text(TTR("Add"));
add_bind_hb->add_child(add_bind);
add_bind->connect("pressed", this, "_add_bind");
Button *del_bind = memnew(Button);
del_bind->set_text(TTR("Remove"));
add_bind_hb->add_child(del_bind);
del_bind->connect("pressed", this, "_remove_bind");
vbc_right->add_margin_child(TTR("Add Extra Call Argument:"), add_bind_hb);
bind_editor = memnew(EditorInspector);
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);
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);
deferred->set_text(TTR("Deferred"));
dstm_hb->add_child(deferred);
oneshot = memnew(CheckButton);
oneshot->set_text(TTR("Oneshot"));
dstm_hb->add_child(oneshot);
set_as_toplevel(true);
cdbinds = memnew(ConnectDialogBinds);
error = memnew(ConfirmationDialog);
add_child(error);
error->get_ok()->set_text(TTR("Close"));
get_ok()->set_text(TTR("Connect"));
}
ConnectDialog::~ConnectDialog() {
memdelete(cdbinds);
}
//ConnectionsDock ==========================
struct _ConnectionsDockMethodInfoSort {
_FORCE_INLINE_ bool operator()(const MethodInfo &a, const MethodInfo &b) const {
return a.name < b.name;
}
};
/*
Post-ConnectDialog callback for creating/editing connections.
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 = selectedNode->get_node(dst_path);
ERR_FAIL_COND(!target);
Connection cToMake;
cToMake.source = connect_dialog->get_source();
cToMake.target = target;
cToMake.signal = connect_dialog->get_signal_name();
cToMake.method = connect_dialog->get_dst_method_name();
cToMake.binds = connect_dialog->get_binds();
bool defer = connect_dialog->get_deferred();
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();
PoolStringArray 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"];
for (int i = 0; i < cToMake.binds.size(); i++) {
script_function_args.append("extra_arg_" + itos(i));
}
}
if (connect_dialog->is_editing()) {
_disconnect(*it);
_connect(cToMake);
} else {
_connect(cToMake);
}
// IMPORTANT NOTE: _disconnect and _connect cause an update_tree,
// which will delete the object "it" is pointing to
it = NULL;
if (add_script_function) {
editor->emit_signal("script_add_function_request", target, cToMake.method, script_function_args);
hide();
}
update_tree();
}
/*
Creates single connection w/ undo-redo functionality.
*/
void ConnectionsDock::_connect(Connection cToMake) {
Node *source = static_cast<Node *>(cToMake.source);
Node *target = static_cast<Node *>(cToMake.target);
if (!source || !target)
return;
undo_redo->create_action(vformat(TTR("Connect '%s' to '%s'"), String(cToMake.signal), String(cToMake.method)));
undo_redo->add_do_method(source, "connect", cToMake.signal, target, cToMake.method, cToMake.binds, cToMake.flags);
undo_redo->add_undo_method(source, "disconnect", cToMake.signal, target, cToMake.method);
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_undo_method(EditorNode::get_singleton()->get_scene_tree_dock()->get_tree_editor(), "update_tree");
undo_redo->commit_action();
}
/*
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
undo_redo->create_action(vformat(TTR("Disconnect '%s' from '%s'"), c.signal, c.method));
undo_redo->add_do_method(selectedNode, "disconnect", c.signal, c.target, c.method);
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_undo_method(EditorNode::get_singleton()->get_scene_tree_dock()->get_tree_editor(), "update_tree");
undo_redo->commit_action();
}
/*
Break all connections of currently selected signal.
Can undo-redo as a single action.
*/
void ConnectionsDock::_disconnect_all() {
TreeItem *item = tree->get_selected();
if (!_is_item_signal(*item))
return;
TreeItem *child = item->get_children();
String signalName = item->get_metadata(0).operator Dictionary()["name"];
undo_redo->create_action(vformat(TTR("Disconnect all from signal: '%s'"), signalName));
while (child) {
Connection c = child->get_metadata(0);
undo_redo->add_do_method(selectedNode, "disconnect", c.signal, c.target, c.method);
undo_redo->add_undo_method(selectedNode, "connect", c.signal, c.target, c.method, c.binds, c.flags);
child = child->get_next();
}
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");
undo_redo->add_undo_method(EditorNode::get_singleton()->get_scene_tree_dock()->get_tree_editor(), "update_tree");
undo_redo->commit_action();
}
void ConnectionsDock::_tree_item_selected() {
TreeItem *item = tree->get_selected();
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)) {
connect_button->set_text(TTR("Connect..."));
connect_button->set_disabled(false);
} else {
connect_button->set_text(TTR("Disconnect"));
connect_button->set_disabled(false);
}
}
void ConnectionsDock::_tree_item_activated() { //"Activation" on double-click.
TreeItem *item = tree->get_selected();
if (!item)
return;
if (_is_item_signal(*item)) {
_open_connection_dialog(*item);
} else {
_go_to_script(*item);
}
}
bool ConnectionsDock::_is_item_signal(TreeItem &item) {
return (item.get_parent() == tree->get_root() || item.get_parent()->get_parent() == tree->get_root());
}
/*
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;
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.
c = '_';
} else {
//Remove any other characters.
midname.remove(i);
i--;
continue;
}
}
midname[i] = c;
}
Node *dst_node = selectedNode->get_owner() ? selectedNode->get_owner() : selectedNode;
StringName dst_method = "_on_" + midname + "_" + signal;
Connection c;
c.source = selectedNode;
c.signal = StringName(signalname);
c.target = dst_node;
c.method = dst_method;
connect_dialog->init(c);
connect_dialog->set_title(TTR("Connect Signal: ") + signalname);
connect_dialog->popup_centered_ratio();
}
/*
Open connection dialog with Connection data to EDIT an existing connection.
*/
void ConnectionsDock::_open_connection_dialog(Connection cToEdit) {
Node *src = static_cast<Node *>(cToEdit.source);
Node *dst = static_cast<Node *>(cToEdit.target);
if (src && dst) {
connect_dialog->init(cToEdit, true);
connect_dialog->set_title(TTR("Edit Connection: ") + cToEdit.signal);
connect_dialog->popup_centered_ratio();
}
}
/*
Open slot method location in script editor.
*/
void ConnectionsDock::_go_to_script(TreeItem &item) {
if (_is_item_signal(item))
return;
Connection c = item.get_metadata(0);
ERR_FAIL_COND(c.source != selectedNode); //shouldn't happen but...bugcheck
if (!c.target)
return;
Ref<Script> script = c.target->get_script();
if (script.is_null())
return;
if (script.is_valid() && ScriptEditor::get_singleton()->script_goto_method(script, c.method)) {
editor->call("_editor_select", EditorNode::EDITOR_SCRIPT);
}
}
void ConnectionsDock::_handle_signal_menu_option(int option) {
TreeItem *item = tree->get_selected();
if (!item)
return;
switch (option) {
case CONNECT: {
_open_connection_dialog(*item);
} break;
case DISCONNECT_ALL: {
StringName signal_name = item->get_metadata(0).operator Dictionary()["name"];
disconnect_all_dialog->set_text(vformat(TTR("Are you sure you want to remove all connections from the \"%s\" signal?"), signal_name));
disconnect_all_dialog->popup_centered();
} break;
}
}
void ConnectionsDock::_handle_slot_menu_option(int option) {
TreeItem *item = tree->get_selected();
if (!item)
return;
switch (option) {
case EDIT: {
Connection c = item->get_metadata(0);
_open_connection_dialog(c);
} break;
case GO_TO_SCRIPT: {
_go_to_script(*item);
} break;
case DISCONNECT: {
_disconnect(*item);
update_tree();
} break;
}
}
void ConnectionsDock::_rmb_pressed(Vector2 position) {
TreeItem *item = tree->get_selected();
if (!item)
return;
Vector2 global_position = tree->get_global_position() + position;
if (_is_item_signal(*item)) {
signal_menu->set_position(global_position);
signal_menu->popup();
} else {
slot_menu->set_position(global_position);
slot_menu->popup();
}
}
void ConnectionsDock::_close() {
hide();
}
void ConnectionsDock::_connect_pressed() {
TreeItem *item = tree->get_selected();
if (!item) {
connect_button->set_disabled(true);
return;
}
if (_is_item_signal(*item)) {
_open_connection_dialog(*item);
} else {
_disconnect(*item);
update_tree();
}
}
void ConnectionsDock::_notification(int p_what) {
if (p_what == EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED) {
update_tree();
}
}
void ConnectionsDock::_bind_methods() {
ClassDB::bind_method("_make_or_edit_connection", &ConnectionsDock::_make_or_edit_connection);
ClassDB::bind_method("_disconnect_all", &ConnectionsDock::_disconnect_all);
ClassDB::bind_method("_tree_item_selected", &ConnectionsDock::_tree_item_selected);
ClassDB::bind_method("_tree_item_activated", &ConnectionsDock::_tree_item_activated);
ClassDB::bind_method("_handle_signal_menu_option", &ConnectionsDock::_handle_signal_menu_option);
ClassDB::bind_method("_handle_slot_menu_option", &ConnectionsDock::_handle_slot_menu_option);
ClassDB::bind_method("_rmb_pressed", &ConnectionsDock::_rmb_pressed);
ClassDB::bind_method("_close", &ConnectionsDock::_close);
ClassDB::bind_method("_connect_pressed", &ConnectionsDock::_connect_pressed);
ClassDB::bind_method("update_tree", &ConnectionsDock::update_tree);
}
void ConnectionsDock::set_node(Node *p_node) {
selectedNode = p_node;
update_tree();
}
void ConnectionsDock::update_tree() {
tree->clear();
if (!selectedNode)
return;
TreeItem *root = tree->create_item();
List<MethodInfo> node_signals;
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;
Ref<Texture> icon;
String name;
if (!did_script) {
Ref<Script> scr = selectedNode->get_script();
if (scr.is_valid()) {
scr->get_script_signal_list(&node_signals);
if (scr->get_path().is_resource_file())
name = scr->get_path().get_file();
else
name = scr->get_class();
if (has_icon(scr->get_class(), "EditorIcons")) {
icon = get_icon(scr->get_class(), "EditorIcons");
}
}
} else {
ClassDB::get_signal_list(base, &node_signals, true);
if (has_icon(base, "EditorIcons")) {
icon = get_icon(base, "EditorIcons");
}
name = base;
}
TreeItem *pitem = NULL;
if (node_signals.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();
}
for (List<MethodInfo>::Element *E = node_signals.front(); E; E = E->next()) {
MethodInfo &mi = E->get();
String signaldesc;
signaldesc = mi.name + "(";
PoolStringArray argnames;
if (mi.arguments.size()) {
signaldesc += " ";
for (int i = 0; i < mi.arguments.size(); i++) {
PropertyInfo &pi = mi.arguments[i];
if (i > 0)
signaldesc += ", ";
String tname = "var";
if (pi.type == Variant::OBJECT && pi.class_name != StringName()) {
tname = pi.class_name.operator String();
} else if (pi.type != Variant::NIL) {
tname = Variant::get_type_name(pi.type);
}
signaldesc += tname + " " + (pi.name == "" ? String("arg " + itos(i)) : pi.name);
argnames.push_back(pi.name + ":" + tname);
}
signaldesc += " ";
}
signaldesc += ")";
TreeItem *item = tree->create_item(pitem);
item->set_text(0, signaldesc);
Dictionary sinfo;
sinfo["name"] = mi.name;
sinfo["args"] = argnames;
item->set_metadata(0, sinfo);
item->set_icon(0, get_icon("Signal", "EditorIcons"));
List<Object::Connection> connections;
selectedNode->get_signal_connection_list(mi.name, &connections);
for (List<Object::Connection>::Element *F = connections.front(); F; F = F->next()) {
Object::Connection &c = F->get();
if (!(c.flags & CONNECT_PERSIST))
continue;
Node *target = Object::cast_to<Node>(c.target);
if (!target)
continue;
String path = String(selectedNode->get_path_to(target)) + " :: " + c.method + "()";
if (c.flags & CONNECT_DEFERRED)
path += " (deferred)";
if (c.flags & CONNECT_ONESHOT)
path += " (oneshot)";
if (c.binds.size()) {
path += " binds( ";
for (int i = 0; i < c.binds.size(); i++) {
if (i > 0)
path += ", ";
path += c.binds[i].operator String();
}
path += " )";
}
TreeItem *item2 = tree->create_item(item);
item2->set_text(0, path);
item2->set_metadata(0, c);
item2->set_icon(0, get_icon("Slot", "EditorIcons"));
}
}
if (!did_script) {
did_script = true;
} else {
base = ClassDB::get_parent_class(base);
}
}
connect_button->set_text(TTR("Connect"));
connect_button->set_disabled(true);
}
ConnectionsDock::ConnectionsDock(EditorNode *p_editor) {
editor = p_editor;
set_name(TTR("Signals"));
VBoxContainer *vbc = this;
tree = memnew(Tree);
tree->set_columns(1);
tree->set_select_mode(Tree::SELECT_ROW);
tree->set_hide_root(true);
vbc->add_child(tree);
tree->set_v_size_flags(SIZE_EXPAND_FILL);
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();
hb->add_child(connect_button);
connect_button->connect("pressed", this, "_connect_pressed");
connect_dialog = memnew(ConnectDialog);
connect_dialog->set_as_toplevel(true);
add_child(connect_dialog);
disconnect_all_dialog = memnew(ConfirmationDialog);
disconnect_all_dialog->set_as_toplevel(true);
add_child(disconnect_all_dialog);
disconnect_all_dialog->connect("confirmed", this, "_disconnect_all");
disconnect_all_dialog->set_text(TTR("Are you sure you want to remove all connections from this signal?"));
signal_menu = memnew(PopupMenu);
add_child(signal_menu);
signal_menu->connect("id_pressed", this, "_handle_signal_menu_option");
signal_menu->add_item(TTR("Connect..."), CONNECT);
signal_menu->add_item(TTR("Disconnect All"), DISCONNECT_ALL);
slot_menu = memnew(PopupMenu);
add_child(slot_menu);
slot_menu->connect("id_pressed", this, "_handle_slot_menu_option");
slot_menu->add_item(TTR("Edit..."), EDIT);
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");
tree->connect("item_rmb_selected", this, "_rmb_pressed");
add_constant_override("separation", 3 * EDSCALE);
}
ConnectionsDock::~ConnectionsDock() {
}