summaryrefslogtreecommitdiff
path: root/scene/main/node.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'scene/main/node.cpp')
-rw-r--r--scene/main/node.cpp278
1 files changed, 197 insertions, 81 deletions
diff --git a/scene/main/node.cpp b/scene/main/node.cpp
index 0c01516032..893621fbc4 100644
--- a/scene/main/node.cpp
+++ b/scene/main/node.cpp
@@ -46,7 +46,7 @@
#include <stdint.h>
-VARIANT_ENUM_CAST(Node::PauseMode);
+VARIANT_ENUM_CAST(Node::ProcessMode);
int Node::orphan_node_count = 0;
@@ -69,14 +69,14 @@ void Node::_notification(int p_notification) {
ERR_FAIL_COND(!get_viewport());
ERR_FAIL_COND(!get_tree());
- if (data.pause_mode == PAUSE_MODE_INHERIT) {
+ if (data.process_mode == PROCESS_MODE_INHERIT) {
if (data.parent) {
- data.pause_owner = data.parent->data.pause_owner;
+ data.process_owner = data.parent->data.process_owner;
} else {
- data.pause_owner = nullptr;
+ data.process_owner = nullptr;
}
} else {
- data.pause_owner = this;
+ data.process_owner = this;
}
if (data.input) {
@@ -110,7 +110,7 @@ void Node::_notification(int p_notification) {
remove_from_group("_vp_unhandled_key_input" + itos(get_viewport()->get_instance_id()));
}
- data.pause_owner = nullptr;
+ data.process_owner = nullptr;
if (data.path_cache) {
memdelete(data.path_cache);
data.path_cache = nullptr;
@@ -367,8 +367,6 @@ void Node::set_physics_process(bool p_process) {
} else {
remove_from_group("physics_process");
}
-
- _change_notify("physics_process");
}
bool Node::is_physics_processing() const {
@@ -387,52 +385,87 @@ void Node::set_physics_process_internal(bool p_process_internal) {
} else {
remove_from_group("physics_process_internal");
}
-
- _change_notify("physics_process_internal");
}
bool Node::is_physics_processing_internal() const {
return data.physics_process_internal;
}
-void Node::set_pause_mode(PauseMode p_mode) {
- if (data.pause_mode == p_mode) {
+void Node::set_process_mode(ProcessMode p_mode) {
+ if (data.process_mode == p_mode) {
return;
}
- bool prev_inherits = data.pause_mode == PAUSE_MODE_INHERIT;
- data.pause_mode = p_mode;
if (!is_inside_tree()) {
- return; //pointless
- }
- if ((data.pause_mode == PAUSE_MODE_INHERIT) == prev_inherits) {
- return; ///nothing changed
+ data.process_mode = p_mode;
+ return;
}
- Node *owner = nullptr;
+ bool prev_can_process = can_process();
+
+ data.process_mode = p_mode;
- if (data.pause_mode == PAUSE_MODE_INHERIT) {
+ if (data.process_mode == PROCESS_MODE_INHERIT) {
if (data.parent) {
- owner = data.parent->data.pause_owner;
+ data.process_owner = data.parent->data.owner;
+ } else {
+ data.process_owner = nullptr;
}
} else {
- owner = this;
+ data.process_owner = this;
}
- _propagate_pause_owner(owner);
+ bool next_can_process = can_process();
+
+ int pause_notification = 0;
+
+ if (prev_can_process && !next_can_process) {
+ pause_notification = NOTIFICATION_PAUSED;
+ } else if (!prev_can_process && next_can_process) {
+ pause_notification = NOTIFICATION_UNPAUSED;
+ }
+
+ _propagate_process_owner(data.process_owner, pause_notification);
+#ifdef TOOLS_ENABLED
+ // This is required for the editor to update the visibility of disabled nodes
+ // Its very expensive during runtime to change, so editor-only
+ if (Engine::get_singleton()->is_editor_hint()) {
+ get_tree()->emit_signal("tree_process_mode_changed");
+ }
+#endif
}
-Node::PauseMode Node::get_pause_mode() const {
- return data.pause_mode;
+void Node::_propagate_pause_notification(bool p_enable) {
+ bool prev_can_process = _can_process(!p_enable);
+ bool next_can_process = _can_process(p_enable);
+
+ if (prev_can_process && !next_can_process) {
+ notification(NOTIFICATION_PAUSED);
+ } else if (!prev_can_process && next_can_process) {
+ notification(NOTIFICATION_UNPAUSED);
+ }
+
+ for (int i = 0; i < data.children.size(); i++) {
+ data.children[i]->_propagate_pause_notification(p_enable);
+ }
}
-void Node::_propagate_pause_owner(Node *p_owner) {
- if (this != p_owner && data.pause_mode != PAUSE_MODE_INHERIT) {
- return;
+Node::ProcessMode Node::get_process_mode() const {
+ return data.process_mode;
+}
+
+void Node::_propagate_process_owner(Node *p_owner, int p_notification) {
+ data.process_owner = p_owner;
+
+ if (p_notification != 0) {
+ notification(p_notification);
}
- data.pause_owner = p_owner;
+
for (int i = 0; i < data.children.size(); i++) {
- data.children[i]->_propagate_pause_owner(p_owner);
+ Node *c = data.children[i];
+ if (c->data.process_mode == PROCESS_MODE_INHERIT) {
+ c->_propagate_process_owner(p_owner, p_notification);
+ }
}
}
@@ -466,7 +499,7 @@ uint16_t Node::rpc_config(const StringName &p_method, MultiplayerAPI::RPCMode p_
nd.name = p_method;
nd.mode = p_mode;
data.rpc_methods.push_back(nd);
- return ((uint16_t)data.rpc_properties.size() - 1) | (1 << 15);
+ return ((uint16_t)data.rpc_methods.size() - 1) | (1 << 15);
} else {
int c_mid = (~(1 << 15)) & mid;
data.rpc_methods.write[c_mid].mode = p_mode;
@@ -555,7 +588,7 @@ Variant Node::_rpc_bind(const Variant **p_args, int p_argcount, Callable::CallEr
return Variant();
}
- if (p_args[0]->get_type() != Variant::STRING) {
+ if (p_args[0]->get_type() != Variant::STRING_NAME) {
r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
r_error.argument = 0;
r_error.expected = Variant::STRING;
@@ -584,7 +617,7 @@ Variant Node::_rpc_id_bind(const Variant **p_args, int p_argcount, Callable::Cal
return Variant();
}
- if (p_args[1]->get_type() != Variant::STRING) {
+ if (p_args[1]->get_type() != Variant::STRING_NAME) {
r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
r_error.argument = 1;
r_error.expected = Variant::STRING;
@@ -607,7 +640,7 @@ Variant Node::_rpc_unreliable_bind(const Variant **p_args, int p_argcount, Calla
return Variant();
}
- if (p_args[0]->get_type() != Variant::STRING) {
+ if (p_args[0]->get_type() != Variant::STRING_NAME) {
r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
r_error.argument = 0;
r_error.expected = Variant::STRING;
@@ -636,7 +669,7 @@ Variant Node::_rpc_unreliable_id_bind(const Variant **p_args, int p_argcount, Ca
return Variant();
}
- if (p_args[1]->get_type() != Variant::STRING) {
+ if (p_args[1]->get_type() != Variant::STRING_NAME) {
r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
r_error.argument = 1;
r_error.expected = Variant::STRING;
@@ -809,30 +842,33 @@ bool Node::can_process_notification(int p_what) const {
bool Node::can_process() const {
ERR_FAIL_COND_V(!is_inside_tree(), false);
+ return _can_process(get_tree()->is_paused());
+}
- if (get_tree()->is_paused()) {
- if (data.pause_mode == PAUSE_MODE_STOP) {
- return false;
- }
- if (data.pause_mode == PAUSE_MODE_PROCESS) {
- return true;
- }
- if (data.pause_mode == PAUSE_MODE_INHERIT) {
- if (!data.pause_owner) {
- return false; //clearly no pause owner by default
- }
-
- if (data.pause_owner->data.pause_mode == PAUSE_MODE_PROCESS) {
- return true;
- }
+bool Node::_can_process(bool p_paused) const {
+ ProcessMode process_mode;
- if (data.pause_owner->data.pause_mode == PAUSE_MODE_STOP) {
- return false;
- }
+ if (data.process_mode == PROCESS_MODE_INHERIT) {
+ if (!data.process_owner) {
+ process_mode = PROCESS_MODE_PAUSABLE;
+ } else {
+ process_mode = data.process_owner->data.process_mode;
}
+ } else {
+ process_mode = data.process_mode;
}
- return true;
+ if (process_mode == PROCESS_MODE_DISABLED) {
+ return false;
+ } else if (process_mode == PROCESS_MODE_ALWAYS) {
+ return true;
+ }
+
+ if (p_paused) {
+ return process_mode == PROCESS_MODE_WHEN_PAUSED;
+ } else {
+ return process_mode == PROCESS_MODE_PAUSABLE;
+ }
}
float Node::get_physics_process_delta_time() const {
@@ -863,8 +899,6 @@ void Node::set_process(bool p_process) {
} else {
remove_from_group("process");
}
-
- _change_notify("process");
}
bool Node::is_processing() const {
@@ -883,8 +917,6 @@ void Node::set_process_internal(bool p_process_internal) {
} else {
remove_from_group("process_internal");
}
-
- _change_notify("process_internal");
}
bool Node::is_processing_internal() const {
@@ -1116,7 +1148,7 @@ void Node::_generate_serial_child_name(const Node *p_child, StringName &name) co
name = p_child->get_class();
// Adjust casing according to project setting. The current type name is expected to be in PascalCase.
- switch (ProjectSettings::get_singleton()->get("node/name_casing").operator int()) {
+ switch (ProjectSettings::get_singleton()->get("editor/node_naming/name_casing").operator int()) {
case NAME_CASING_PASCAL_CASE:
break;
case NAME_CASING_CAMEL_CASE: {
@@ -1410,7 +1442,15 @@ Node *Node::get_node_or_null(const NodePath &p_path) const {
Node *Node::get_node(const NodePath &p_path) const {
Node *node = get_node_or_null(p_path);
- ERR_FAIL_COND_V_MSG(!node, nullptr, "Node not found: " + p_path + ".");
+
+ if (p_path.is_absolute()) {
+ ERR_FAIL_COND_V_MSG(!node, nullptr,
+ vformat(R"(Node not found: "%s" (absolute path attempted from "%s").)", p_path, get_path()));
+ } else {
+ ERR_FAIL_COND_V_MSG(!node, nullptr,
+ vformat(R"(Node not found: "%s" (relative to "%s").)", p_path, get_path()));
+ }
+
return node;
}
@@ -1906,15 +1946,11 @@ String Node::get_filename() const {
}
void Node::set_editor_description(const String &p_editor_description) {
- set_meta("_editor_description_", p_editor_description);
+ data.editor_description = p_editor_description;
}
String Node::get_editor_description() const {
- if (has_meta("_editor_description_")) {
- return get_meta("_editor_description_");
- } else {
- return "";
- }
+ return data.editor_description;
}
void Node::set_editable_instance(Node *p_node, bool p_editable) {
@@ -1938,6 +1974,23 @@ bool Node::is_editable_instance(const Node *p_node) const {
return p_node->data.editable_instance;
}
+Node *Node::get_deepest_editable_node(Node *p_start_node) const {
+ ERR_FAIL_NULL_V(p_start_node, nullptr);
+ ERR_FAIL_COND_V(!is_a_parent_of(p_start_node), nullptr);
+
+ Node const *iterated_item = p_start_node;
+ Node *node = p_start_node;
+
+ while (iterated_item->get_owner() && iterated_item->get_owner() != this) {
+ if (!is_editable_instance(iterated_item->get_owner()))
+ node = iterated_item->get_owner();
+
+ iterated_item = iterated_item->get_owner();
+ }
+
+ return node;
+}
+
void Node::set_scene_instance_state(const Ref<SceneState> &p_state) {
data.instance_state = p_state;
}
@@ -2146,8 +2199,17 @@ Node *Node::duplicate(int p_flags) const {
#ifdef TOOLS_ENABLED
Node *Node::duplicate_from_editor(Map<const Node *, Node *> &r_duplimap) const {
+ return duplicate_from_editor(r_duplimap, Map<RES, RES>());
+}
+
+Node *Node::duplicate_from_editor(Map<const Node *, Node *> &r_duplimap, const Map<RES, RES> &p_resource_remap) const {
Node *dupe = _duplicate(DUPLICATE_SIGNALS | DUPLICATE_GROUPS | DUPLICATE_SCRIPTS | DUPLICATE_USE_INSTANCING | DUPLICATE_FROM_EDITOR, &r_duplimap);
+ // This is used by SceneTreeDock's paste functionality. When pasting to foreign scene, resources are duplicated.
+ if (!p_resource_remap.is_empty()) {
+ remap_node_resources(dupe, p_resource_remap);
+ }
+
// Duplication of signals must happen after all the node descendants have been copied,
// because re-targeting of connections from some descendant to another is not possible
// if the emitter node comes later in tree order than the receiver
@@ -2155,6 +2217,54 @@ Node *Node::duplicate_from_editor(Map<const Node *, Node *> &r_duplimap) const {
return dupe;
}
+
+void Node::remap_node_resources(Node *p_node, const Map<RES, RES> &p_resource_remap) const {
+ List<PropertyInfo> props;
+ p_node->get_property_list(&props);
+
+ for (List<PropertyInfo>::Element *E = props.front(); E; E = E->next()) {
+ if (!(E->get().usage & PROPERTY_USAGE_STORAGE)) {
+ continue;
+ }
+
+ Variant v = p_node->get(E->get().name);
+ if (v.is_ref()) {
+ RES res = v;
+ if (res.is_valid()) {
+ if (p_resource_remap.has(res)) {
+ p_node->set(E->get().name, p_resource_remap[res]);
+ remap_nested_resources(res, p_resource_remap);
+ }
+ }
+ }
+ }
+
+ for (int i = 0; i < p_node->get_child_count(); i++) {
+ remap_node_resources(p_node->get_child(i), p_resource_remap);
+ }
+}
+
+void Node::remap_nested_resources(RES p_resource, const Map<RES, RES> &p_resource_remap) const {
+ List<PropertyInfo> props;
+ p_resource->get_property_list(&props);
+
+ for (List<PropertyInfo>::Element *E = props.front(); E; E = E->next()) {
+ if (!(E->get().usage & PROPERTY_USAGE_STORAGE)) {
+ continue;
+ }
+
+ Variant v = p_resource->get(E->get().name);
+ if (v.is_ref()) {
+ RES res = v;
+ if (res.is_valid()) {
+ if (p_resource_remap.has(res)) {
+ p_resource->set(E->get().name, p_resource_remap[res]);
+ remap_nested_resources(res, p_resource_remap);
+ }
+ }
+ }
+ }
+}
#endif
void Node::_duplicate_and_reown(Node *p_new_parent, const Map<Node *, Node *> &p_reown_map) const {
@@ -2676,10 +2786,10 @@ void Node::request_ready() {
}
void Node::_bind_methods() {
- GLOBAL_DEF("node/name_num_separator", 0);
- ProjectSettings::get_singleton()->set_custom_property_info("node/name_num_separator", PropertyInfo(Variant::INT, "node/name_num_separator", PROPERTY_HINT_ENUM, "None,Space,Underscore,Dash"));
- GLOBAL_DEF("node/name_casing", NAME_CASING_PASCAL_CASE);
- ProjectSettings::get_singleton()->set_custom_property_info("node/name_casing", PropertyInfo(Variant::INT, "node/name_casing", PROPERTY_HINT_ENUM, "PascalCase,camelCase,snake_case"));
+ GLOBAL_DEF("editor/node_naming/name_num_separator", 0);
+ ProjectSettings::get_singleton()->set_custom_property_info("editor/node_naming/name_num_separator", PropertyInfo(Variant::INT, "editor/node_naming/name_num_separator", PROPERTY_HINT_ENUM, "None,Space,Underscore,Dash"));
+ GLOBAL_DEF("editor/node_naming/name_casing", NAME_CASING_PASCAL_CASE);
+ ProjectSettings::get_singleton()->set_custom_property_info("editor/node_naming/name_casing", PropertyInfo(Variant::INT, "editor/node_naming/name_casing", PROPERTY_HINT_ENUM, "PascalCase,camelCase,snake_case"));
ClassDB::bind_method(D_METHOD("add_sibling", "sibling", "legible_unique_name"), &Node::add_sibling, DEFVAL(false));
@@ -2734,8 +2844,8 @@ void Node::_bind_methods() {
ClassDB::bind_method(D_METHOD("is_processing_unhandled_input"), &Node::is_processing_unhandled_input);
ClassDB::bind_method(D_METHOD("set_process_unhandled_key_input", "enable"), &Node::set_process_unhandled_key_input);
ClassDB::bind_method(D_METHOD("is_processing_unhandled_key_input"), &Node::is_processing_unhandled_key_input);
- ClassDB::bind_method(D_METHOD("set_pause_mode", "mode"), &Node::set_pause_mode);
- ClassDB::bind_method(D_METHOD("get_pause_mode"), &Node::get_pause_mode);
+ ClassDB::bind_method(D_METHOD("set_process_mode", "mode"), &Node::set_process_mode);
+ ClassDB::bind_method(D_METHOD("get_process_mode"), &Node::get_process_mode);
ClassDB::bind_method(D_METHOD("can_process"), &Node::can_process);
ClassDB::bind_method(D_METHOD("print_stray_nodes"), &Node::_print_stray_nodes);
@@ -2773,12 +2883,12 @@ void Node::_bind_methods() {
ClassDB::bind_method(D_METHOD("rpc_config", "method", "mode"), &Node::rpc_config);
ClassDB::bind_method(D_METHOD("rset_config", "property", "mode"), &Node::rset_config);
- ClassDB::bind_method(D_METHOD("_set_editor_description", "editor_description"), &Node::set_editor_description);
- ClassDB::bind_method(D_METHOD("_get_editor_description"), &Node::get_editor_description);
- ADD_PROPERTY(PropertyInfo(Variant::STRING, "editor_description", PROPERTY_HINT_MULTILINE_TEXT, "", PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_editor_description", "_get_editor_description");
+ ClassDB::bind_method(D_METHOD("set_editor_description", "editor_description"), &Node::set_editor_description);
+ ClassDB::bind_method(D_METHOD("get_editor_description"), &Node::get_editor_description);
ClassDB::bind_method(D_METHOD("_set_import_path", "import_path"), &Node::set_import_path);
ClassDB::bind_method(D_METHOD("_get_import_path"), &Node::get_import_path);
+
ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "_import_path", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_import_path", "_get_import_path");
{
@@ -2842,9 +2952,11 @@ void Node::_bind_methods() {
BIND_CONSTANT(NOTIFICATION_APPLICATION_FOCUS_OUT);
BIND_CONSTANT(NOTIFICATION_TEXT_SERVER_CHANGED);
- BIND_ENUM_CONSTANT(PAUSE_MODE_INHERIT);
- BIND_ENUM_CONSTANT(PAUSE_MODE_STOP);
- BIND_ENUM_CONSTANT(PAUSE_MODE_PROCESS);
+ BIND_ENUM_CONSTANT(PROCESS_MODE_INHERIT);
+ BIND_ENUM_CONSTANT(PROCESS_MODE_PAUSABLE);
+ BIND_ENUM_CONSTANT(PROCESS_MODE_WHEN_PAUSED);
+ BIND_ENUM_CONSTANT(PROCESS_MODE_ALWAYS);
+ BIND_ENUM_CONSTANT(PROCESS_MODE_DISABLED);
BIND_ENUM_CONSTANT(DUPLICATE_SIGNALS);
BIND_ENUM_CONSTANT(DUPLICATE_GROUPS);
@@ -2857,15 +2969,19 @@ void Node::_bind_methods() {
ADD_SIGNAL(MethodInfo("tree_exiting"));
ADD_SIGNAL(MethodInfo("tree_exited"));
- ADD_PROPERTY(PropertyInfo(Variant::INT, "pause_mode", PROPERTY_HINT_ENUM, "Inherit,Stop,Process"), "set_pause_mode", "get_pause_mode");
-
ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "name", PROPERTY_HINT_NONE, "", 0), "set_name", "get_name");
ADD_PROPERTY(PropertyInfo(Variant::STRING, "filename", PROPERTY_HINT_NONE, "", 0), "set_filename", "get_filename");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "owner", PROPERTY_HINT_RESOURCE_TYPE, "Node", 0), "set_owner", "get_owner");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "multiplayer", PROPERTY_HINT_RESOURCE_TYPE, "MultiplayerAPI", 0), "", "get_multiplayer");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "custom_multiplayer", PROPERTY_HINT_RESOURCE_TYPE, "MultiplayerAPI", 0), "set_custom_multiplayer", "get_custom_multiplayer");
+
+ ADD_GROUP("Process", "process_");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "process_mode", PROPERTY_HINT_ENUM, "Inherit,Pausable,WhenPaused,Always,Disabled"), "set_process_mode", "get_process_mode");
ADD_PROPERTY(PropertyInfo(Variant::INT, "process_priority"), "set_process_priority", "get_process_priority");
+ ADD_GROUP("Editor Description", "editor_");
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "editor_description", PROPERTY_HINT_MULTILINE_TEXT, "", PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_INTERNAL), "set_editor_description", "get_editor_description");
+
BIND_VMETHOD(MethodInfo("_process", PropertyInfo(Variant::FLOAT, "delta")));
BIND_VMETHOD(MethodInfo("_physics_process", PropertyInfo(Variant::FLOAT, "delta")));
BIND_VMETHOD(MethodInfo("_enter_tree"));
@@ -2878,7 +2994,7 @@ void Node::_bind_methods() {
}
String Node::_get_name_num_separator() {
- switch (ProjectSettings::get_singleton()->get("node/name_num_separator").operator int()) {
+ switch (ProjectSettings::get_singleton()->get("editor/node_naming/name_num_separator").operator int()) {
case 0:
return "";
case 1: