Browse Source

Add a flag to make the connection automatically emit the source object.

Mainly used to improve the connection dialog.

Not implemented in `emit_signalp()`, append the source object when a PackScene is instantiated.
Rindbee 3 years ago
parent
commit
5e2396e001

+ 1 - 0
core/object/object.cpp

@@ -1924,6 +1924,7 @@ void Object::_bind_methods() {
 	BIND_ENUM_CONSTANT(CONNECT_PERSIST);
 	BIND_ENUM_CONSTANT(CONNECT_PERSIST);
 	BIND_ENUM_CONSTANT(CONNECT_ONE_SHOT);
 	BIND_ENUM_CONSTANT(CONNECT_ONE_SHOT);
 	BIND_ENUM_CONSTANT(CONNECT_REFERENCE_COUNTED);
 	BIND_ENUM_CONSTANT(CONNECT_REFERENCE_COUNTED);
+	BIND_ENUM_CONSTANT(CONNECT_APPEND_SOURCE_OBJECT);
 }
 }
 
 
 void Object::set_deferred(const StringName &p_property, const Variant &p_value) {
 void Object::set_deferred(const StringName &p_property, const Variant &p_value) {

+ 3 - 2
core/object/object.h

@@ -573,10 +573,11 @@ public:
 
 
 	enum ConnectFlags {
 	enum ConnectFlags {
 		CONNECT_DEFERRED = 1,
 		CONNECT_DEFERRED = 1,
-		CONNECT_PERSIST = 2, // hint for scene to save this connection
+		CONNECT_PERSIST = 2, // Hint for scene to save this connection.
 		CONNECT_ONE_SHOT = 4,
 		CONNECT_ONE_SHOT = 4,
 		CONNECT_REFERENCE_COUNTED = 8,
 		CONNECT_REFERENCE_COUNTED = 8,
-		CONNECT_INHERITED = 16, // Used in editor builds.
+		CONNECT_APPEND_SOURCE_OBJECT = 16,
+		CONNECT_INHERITED = 32, // Used in editor builds.
 	};
 	};
 
 
 	struct Connection {
 	struct Connection {

+ 3 - 0
doc/classes/Object.xml

@@ -1039,5 +1039,8 @@
 		<constant name="CONNECT_REFERENCE_COUNTED" value="8" enum="ConnectFlags">
 		<constant name="CONNECT_REFERENCE_COUNTED" value="8" enum="ConnectFlags">
 			Reference-counted connections can be assigned to the same [Callable] multiple times. Each disconnection decreases the internal counter. The signal fully disconnects only when the counter reaches 0.
 			Reference-counted connections can be assigned to the same [Callable] multiple times. Each disconnection decreases the internal counter. The signal fully disconnects only when the counter reaches 0.
 		</constant>
 		</constant>
+		<constant name="CONNECT_APPEND_SOURCE_OBJECT" value="16" enum="ConnectFlags">
+			The source object is automatically bound when a [PackedScene] is instantiated. If this flag bit is enabled, the source object will be appended right after the original arguments of the signal.
+		</constant>
 	</constants>
 	</constants>
 </class>
 </class>

+ 64 - 10
editor/connections_dialog.cpp

@@ -46,6 +46,7 @@
 #include "editor/themes/editor_scale.h"
 #include "editor/themes/editor_scale.h"
 #include "scene/gui/button.h"
 #include "scene/gui/button.h"
 #include "scene/gui/check_box.h"
 #include "scene/gui/check_box.h"
+#include "scene/gui/flow_container.h"
 #include "scene/gui/label.h"
 #include "scene/gui/label.h"
 #include "scene/gui/line_edit.h"
 #include "scene/gui/line_edit.h"
 #include "scene/gui/margin_container.h"
 #include "scene/gui/margin_container.h"
@@ -195,6 +196,8 @@ void ConnectDialog::_unbind_count_changed(double p_count) {
 			e->set_read_only(p_count > 0);
 			e->set_read_only(p_count > 0);
 		}
 		}
 	}
 	}
+
+	append_source->set_disabled(p_count > 0);
 }
 }
 
 
 void ConnectDialog::_method_selected() {
 void ConnectDialog::_method_selected() {
@@ -626,6 +629,10 @@ bool ConnectDialog::get_one_shot() const {
 	return one_shot->is_pressed();
 	return one_shot->is_pressed();
 }
 }
 
 
+bool ConnectDialog::get_append_source() const {
+	return !append_source->is_disabled() && append_source->is_pressed();
+}
+
 /*
 /*
  * Returns true if ConnectDialog is being used to edit an existing connection.
  * Returns true if ConnectDialog is being used to edit an existing connection.
  */
  */
@@ -667,14 +674,15 @@ void ConnectDialog::init(const ConnectionData &p_cd, const PackedStringArray &p_
 
 
 	_update_ok_enabled();
 	_update_ok_enabled();
 
 
-	bool b_deferred = (p_cd.flags & CONNECT_DEFERRED) == CONNECT_DEFERRED;
-	bool b_oneshot = (p_cd.flags & CONNECT_ONE_SHOT) == CONNECT_ONE_SHOT;
+	bool b_deferred = (p_cd.flags & CONNECT_DEFERRED);
+	bool b_oneshot = (p_cd.flags & CONNECT_ONE_SHOT);
+	bool b_append_source = (p_cd.flags & CONNECT_APPEND_SOURCE_OBJECT);
 
 
 	deferred->set_pressed(b_deferred);
 	deferred->set_pressed(b_deferred);
 	one_shot->set_pressed(b_oneshot);
 	one_shot->set_pressed(b_oneshot);
+	append_source->set_pressed(b_append_source);
 
 
 	unbind_count->set_max(p_signal_args.size());
 	unbind_count->set_max(p_signal_args.size());
-
 	unbind_count->set_value(p_cd.unbinds);
 	unbind_count->set_value(p_cd.unbinds);
 	_unbind_count_changed(p_cd.unbinds);
 	_unbind_count_changed(p_cd.unbinds);
 
 
@@ -892,20 +900,23 @@ ConnectDialog::ConnectDialog() {
 	advanced->set_pressed(EditorSettings::get_singleton()->get_project_metadata("editor_metadata", "use_advanced_connections", false));
 	advanced->set_pressed(EditorSettings::get_singleton()->get_project_metadata("editor_metadata", "use_advanced_connections", false));
 	advanced->connect(SceneStringName(pressed), callable_mp(this, &ConnectDialog::_advanced_pressed));
 	advanced->connect(SceneStringName(pressed), callable_mp(this, &ConnectDialog::_advanced_pressed));
 
 
-	HBoxContainer *hbox = memnew(HBoxContainer);
-	vbc_right->add_child(hbox);
+	FlowContainer *fc_flags = memnew(FlowContainer);
+	vbc_right->add_child(fc_flags);
 
 
 	deferred = memnew(CheckBox);
 	deferred = memnew(CheckBox);
-	deferred->set_h_size_flags(0);
 	deferred->set_text(TTR("Deferred"));
 	deferred->set_text(TTR("Deferred"));
 	deferred->set_tooltip_text(TTR("Defers the signal, storing it in a queue and only firing it at idle time."));
 	deferred->set_tooltip_text(TTR("Defers the signal, storing it in a queue and only firing it at idle time."));
-	hbox->add_child(deferred);
+	fc_flags->add_child(deferred);
 
 
 	one_shot = memnew(CheckBox);
 	one_shot = memnew(CheckBox);
-	one_shot->set_h_size_flags(0);
 	one_shot->set_text(TTR("One Shot"));
 	one_shot->set_text(TTR("One Shot"));
 	one_shot->set_tooltip_text(TTR("Disconnects the signal after its first emission."));
 	one_shot->set_tooltip_text(TTR("Disconnects the signal after its first emission."));
-	hbox->add_child(one_shot);
+	fc_flags->add_child(one_shot);
+
+	append_source = memnew(CheckBox);
+	append_source->set_text(TTRC("Append Source"));
+	append_source->set_tooltip_text(TTRC("The source object is automatically sent when the signal is emitted."));
+	fc_flags->add_child(append_source);
 
 
 	cdbinds = memnew(ConnectDialogBinds);
 	cdbinds = memnew(ConnectDialogBinds);
 
 
@@ -961,7 +972,8 @@ void ConnectionsDock::_make_or_edit_connection() {
 	}
 	}
 	bool b_deferred = connect_dialog->get_deferred();
 	bool b_deferred = connect_dialog->get_deferred();
 	bool b_oneshot = connect_dialog->get_one_shot();
 	bool b_oneshot = connect_dialog->get_one_shot();
-	cd.flags = CONNECT_PERSIST | (b_deferred ? CONNECT_DEFERRED : 0) | (b_oneshot ? CONNECT_ONE_SHOT : 0);
+	bool b_append_source = connect_dialog->get_append_source();
+	cd.flags = CONNECT_PERSIST | (b_deferred ? CONNECT_DEFERRED : 0) | (b_oneshot ? CONNECT_ONE_SHOT : 0) | (b_append_source ? CONNECT_APPEND_SOURCE_OBJECT : 0);
 
 
 	// If the function is found in target's own script, check the editor setting
 	// If the function is found in target's own script, check the editor setting
 	// to determine if the script should be opened.
 	// to determine if the script should be opened.
@@ -1003,6 +1015,45 @@ void ConnectionsDock::_make_or_edit_connection() {
 	if (add_script_function_request) {
 	if (add_script_function_request) {
 		PackedStringArray script_function_args = connect_dialog->get_signal_args();
 		PackedStringArray script_function_args = connect_dialog->get_signal_args();
 		script_function_args.resize(script_function_args.size() - cd.unbinds);
 		script_function_args.resize(script_function_args.size() - cd.unbinds);
+
+		// Append the source.
+		if (b_append_source) {
+			String class_name = cd.source->get_class();
+			bool found = false;
+
+			Ref<Script> source_script = cd.source->get_script();
+			if (source_script.is_valid()) {
+				found = source_script->has_script_signal(cd.signal);
+				if (found) {
+					// Check global name in script inheritance chain.
+					bool need_check = found;
+					Ref<Script> base_script = source_script->get_base_script();
+					while (base_script.is_valid()) {
+						need_check = base_script->has_script_signal(cd.signal);
+						if (!need_check) {
+							break;
+						}
+						source_script = base_script;
+						base_script = source_script->get_base_script();
+					}
+					class_name = source_script->get_global_name();
+				}
+			}
+
+			if (!found) {
+				while (!class_name.is_empty()) {
+					// Search in ClassDB according to the inheritance chain.
+					found = ClassDB::has_signal(class_name, cd.signal, true);
+					if (found) {
+						break;
+					}
+					class_name = ClassDB::get_parent_class(class_name);
+				}
+			}
+
+			script_function_args.push_back("source:" + class_name);
+		}
+
 		for (int i = 0; i < cd.binds.size(); i++) {
 		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()));
 			script_function_args.push_back("extra_arg_" + itos(i) + ":" + Variant::get_type_name(cd.binds[i].get_type()));
 		}
 		}
@@ -1587,6 +1638,9 @@ void ConnectionsDock::update_tree() {
 				if (cd.flags & CONNECT_ONE_SHOT) {
 				if (cd.flags & CONNECT_ONE_SHOT) {
 					path += " (one-shot)";
 					path += " (one-shot)";
 				}
 				}
+				if (cd.flags & CONNECT_APPEND_SOURCE_OBJECT) {
+					path += " (source)";
+				}
 				if (cd.unbinds > 0) {
 				if (cd.unbinds > 0) {
 					path += " unbinds(" + itos(cd.unbinds) + ")";
 					path += " unbinds(" + itos(cd.unbinds) + ")";
 				} else if (!cd.binds.is_empty()) {
 				} else if (!cd.binds.is_empty()) {

+ 8 - 0
editor/connections_dialog.h

@@ -71,6 +71,12 @@ public:
 				CallableCustomBind *ccb = dynamic_cast<CallableCustomBind *>(p_connection.callable.get_custom());
 				CallableCustomBind *ccb = dynamic_cast<CallableCustomBind *>(p_connection.callable.get_custom());
 				if (ccb) {
 				if (ccb) {
 					binds = ccb->get_binds();
 					binds = ccb->get_binds();
+
+					// The source object may already be bound, ignore it to prevent display of the source object.
+					if ((flags & CONNECT_APPEND_SOURCE_OBJECT) && (source == binds[0])) {
+						binds.remove_at(0);
+					}
+
 					base_callable = ccb->get_callable();
 					base_callable = ccb->get_callable();
 				}
 				}
 
 
@@ -130,6 +136,7 @@ private:
 	OptionButton *type_list = nullptr;
 	OptionButton *type_list = nullptr;
 	CheckBox *deferred = nullptr;
 	CheckBox *deferred = nullptr;
 	CheckBox *one_shot = nullptr;
 	CheckBox *one_shot = nullptr;
+	CheckBox *append_source = nullptr;
 	CheckButton *advanced = nullptr;
 	CheckButton *advanced = nullptr;
 	Vector<Control *> bind_controls;
 	Vector<Control *> bind_controls;
 
 
@@ -177,6 +184,7 @@ public:
 
 
 	bool get_deferred() const;
 	bool get_deferred() const;
 	bool get_one_shot() const;
 	bool get_one_shot() const;
+	bool get_append_source() const;
 	bool is_editing() const;
 	bool is_editing() const;
 
 
 	virtual void shortcut_input(const Ref<InputEvent> &p_event) override;
 	virtual void shortcut_input(const Ref<InputEvent> &p_event) override;

+ 16 - 9
scene/resources/packed_scene.cpp

@@ -592,20 +592,21 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const {
 		Callable callable(cto, snames[c.method]);
 		Callable callable(cto, snames[c.method]);
 		if (c.unbinds > 0) {
 		if (c.unbinds > 0) {
 			callable = callable.unbind(c.unbinds);
 			callable = callable.unbind(c.unbinds);
-		} else if (!c.binds.is_empty()) {
-			Vector<Variant> binds;
-			if (c.binds.size()) {
-				binds.resize(c.binds.size());
+		} else {
+			Array binds;
+			if (c.flags & CONNECT_APPEND_SOURCE_OBJECT) {
+				binds.push_back(cfrom);
+			}
+
+			if (!c.binds.is_empty()) {
 				for (int j = 0; j < c.binds.size(); j++) {
 				for (int j = 0; j < c.binds.size(); j++) {
-					binds.write[j] = props[c.binds[j]];
+					binds.push_back(props[c.binds[j]]);
 				}
 				}
 			}
 			}
 
 
-			const Variant **argptrs = (const Variant **)alloca(sizeof(Variant *) * binds.size());
-			for (int j = 0; j < binds.size(); j++) {
-				argptrs[j] = &binds[j];
+			if (!binds.is_empty()) {
+				callable = callable.bindv(binds);
 			}
 			}
-			callable = callable.bindp(argptrs, binds.size());
 		}
 		}
 
 
 		cfrom->connect(snames[c.signal], callable, CONNECT_PERSIST | c.flags | (p_edit_state == GEN_EDIT_STATE_MAIN ? 0 : CONNECT_INHERITED));
 		cfrom->connect(snames[c.signal], callable, CONNECT_PERSIST | c.flags | (p_edit_state == GEN_EDIT_STATE_MAIN ? 0 : CONNECT_INHERITED));
@@ -1079,6 +1080,12 @@ Error SceneState::_parse_connections(Node *p_owner, Node *p_node, HashMap<String
 				CallableCustomBind *ccb = dynamic_cast<CallableCustomBind *>(c.callable.get_custom());
 				CallableCustomBind *ccb = dynamic_cast<CallableCustomBind *>(c.callable.get_custom());
 				if (ccb) {
 				if (ccb) {
 					binds = ccb->get_binds();
 					binds = ccb->get_binds();
+
+					// The source object may already be bound, ignore it to avoid saving the source object.
+					if ((c.flags & CONNECT_APPEND_SOURCE_OBJECT) && (p_node == binds[0])) {
+						binds.remove_at(0);
+					}
+
 					base_callable = ccb->get_callable();
 					base_callable = ccb->get_callable();
 				}
 				}