diff options
Diffstat (limited to 'scene/main')
-rw-r--r-- | scene/main/canvas_item.cpp | 90 | ||||
-rw-r--r-- | scene/main/canvas_item.h | 30 | ||||
-rw-r--r-- | scene/main/canvas_layer.cpp | 1 | ||||
-rw-r--r-- | scene/main/http_request.cpp | 146 | ||||
-rw-r--r-- | scene/main/http_request.h | 4 | ||||
-rw-r--r-- | scene/main/instance_placeholder.cpp | 2 | ||||
-rw-r--r-- | scene/main/missing_node.cpp | 4 | ||||
-rw-r--r-- | scene/main/missing_node.h | 2 | ||||
-rw-r--r-- | scene/main/multiplayer_api.cpp | 62 | ||||
-rw-r--r-- | scene/main/multiplayer_peer.cpp | 23 | ||||
-rw-r--r-- | scene/main/multiplayer_peer.h | 13 | ||||
-rw-r--r-- | scene/main/node.cpp | 237 | ||||
-rw-r--r-- | scene/main/node.h | 20 | ||||
-rw-r--r-- | scene/main/scene_tree.cpp | 131 | ||||
-rw-r--r-- | scene/main/scene_tree.h | 4 | ||||
-rw-r--r-- | scene/main/shader_globals_override.cpp | 4 | ||||
-rw-r--r-- | scene/main/shader_globals_override.h | 2 | ||||
-rw-r--r-- | scene/main/timer.cpp | 4 | ||||
-rw-r--r-- | scene/main/timer.h | 2 | ||||
-rw-r--r-- | scene/main/viewport.cpp | 246 | ||||
-rw-r--r-- | scene/main/viewport.h | 18 | ||||
-rw-r--r-- | scene/main/window.cpp | 151 | ||||
-rw-r--r-- | scene/main/window.h | 6 |
23 files changed, 742 insertions, 460 deletions
diff --git a/scene/main/canvas_item.cpp b/scene/main/canvas_item.cpp index 05d86f77f2..7bcd4721fc 100644 --- a/scene/main/canvas_item.cpp +++ b/scene/main/canvas_item.cpp @@ -168,9 +168,6 @@ Transform2D CanvasItem::get_screen_transform() const { } Transform2D CanvasItem::get_global_transform() const { -#ifdef DEBUG_ENABLED - ERR_FAIL_COND_V(!is_inside_tree(), get_transform()); -#endif if (global_invalid) { const CanvasItem *pi = get_parent_item(); if (pi) { @@ -222,6 +219,7 @@ void CanvasItem::_enter_canvas() { } RenderingServer::get_singleton()->canvas_item_set_parent(canvas_item, canvas); + RenderingServer::get_singleton()->canvas_item_set_visibility_layer(canvas_item, visibility_layer); canvas_group = "root_canvas" + itos(canvas.get_id()); @@ -239,6 +237,7 @@ void CanvasItem::_enter_canvas() { canvas_layer = parent->canvas_layer; RenderingServer::get_singleton()->canvas_item_set_parent(canvas_item, parent->get_canvas_item()); RenderingServer::get_singleton()->canvas_item_set_draw_index(canvas_item, get_index()); + RenderingServer::get_singleton()->canvas_item_set_visibility_layer(canvas_item, visibility_layer); } pending_update = false; @@ -980,14 +979,19 @@ void CanvasItem::_bind_methods() { ClassDB::bind_method(D_METHOD("make_canvas_position_local", "screen_point"), &CanvasItem::make_canvas_position_local); ClassDB::bind_method(D_METHOD("make_input_local", "event"), &CanvasItem::make_input_local); + ClassDB::bind_method(D_METHOD("set_visibility_layer", "layer"), &CanvasItem::set_visibility_layer); + ClassDB::bind_method(D_METHOD("get_visibility_layer"), &CanvasItem::get_visibility_layer); + ClassDB::bind_method(D_METHOD("set_visibility_layer_bit", "layer", "enabled"), &CanvasItem::set_visibility_layer_bit); + ClassDB::bind_method(D_METHOD("get_visibility_layer_bit", "layer"), &CanvasItem::get_visibility_layer_bit); + ClassDB::bind_method(D_METHOD("set_texture_filter", "mode"), &CanvasItem::set_texture_filter); ClassDB::bind_method(D_METHOD("get_texture_filter"), &CanvasItem::get_texture_filter); ClassDB::bind_method(D_METHOD("set_texture_repeat", "mode"), &CanvasItem::set_texture_repeat); ClassDB::bind_method(D_METHOD("get_texture_repeat"), &CanvasItem::get_texture_repeat); - ClassDB::bind_method(D_METHOD("set_clip_children", "enable"), &CanvasItem::set_clip_children); - ClassDB::bind_method(D_METHOD("is_clipping_children"), &CanvasItem::is_clipping_children); + ClassDB::bind_method(D_METHOD("set_clip_children_mode", "mode"), &CanvasItem::set_clip_children_mode); + ClassDB::bind_method(D_METHOD("get_clip_children_mode"), &CanvasItem::get_clip_children_mode); GDVIRTUAL_BIND(_draw); @@ -997,8 +1001,9 @@ void CanvasItem::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::COLOR, "self_modulate"), "set_self_modulate", "get_self_modulate"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "show_behind_parent"), "set_draw_behind_parent", "is_draw_behind_parent_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "top_level"), "set_as_top_level", "is_set_as_top_level"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "clip_children"), "set_clip_children", "is_clipping_children"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "clip_children", PROPERTY_HINT_ENUM, "Disabled,Clip Only,Clip + Draw"), "set_clip_children_mode", "get_clip_children_mode"); ADD_PROPERTY(PropertyInfo(Variant::INT, "light_mask", PROPERTY_HINT_LAYERS_2D_RENDER), "set_light_mask", "get_light_mask"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "visibility_layer", PROPERTY_HINT_LAYERS_2D_RENDER), "set_visibility_layer", "get_visibility_layer"); ADD_GROUP("Texture", "texture_"); ADD_PROPERTY(PropertyInfo(Variant::INT, "texture_filter", PROPERTY_HINT_ENUM, "Inherit,Nearest,Linear,Nearest Mipmap,Linear Mipmap,Nearest Mipmap Anisotropic,Linear Mipmap Anisotropic"), "set_texture_filter", "get_texture_filter"); @@ -1035,6 +1040,11 @@ void CanvasItem::_bind_methods() { BIND_ENUM_CONSTANT(TEXTURE_REPEAT_ENABLED); BIND_ENUM_CONSTANT(TEXTURE_REPEAT_MIRROR); BIND_ENUM_CONSTANT(TEXTURE_REPEAT_MAX); + + BIND_ENUM_CONSTANT(CLIP_CHILDREN_DISABLED); + BIND_ENUM_CONSTANT(CLIP_CHILDREN_ONLY); + BIND_ENUM_CONSTANT(CLIP_CHILDREN_AND_DRAW); + BIND_ENUM_CONSTANT(CLIP_CHILDREN_MAX); } Transform2D CanvasItem::get_canvas_transform() const { @@ -1092,7 +1102,30 @@ int CanvasItem::get_canvas_layer() const { } } -void CanvasItem::_update_texture_filter_changed(bool p_propagate) { +void CanvasItem::set_visibility_layer(uint32_t p_visibility_layer) { + visibility_layer = p_visibility_layer; + RenderingServer::get_singleton()->canvas_item_set_visibility_layer(canvas_item, p_visibility_layer); +} + +uint32_t CanvasItem::get_visibility_layer() const { + return visibility_layer; +} + +void CanvasItem::set_visibility_layer_bit(uint32_t p_visibility_layer, bool p_enable) { + ERR_FAIL_UNSIGNED_INDEX(p_visibility_layer, 32); + if (p_enable) { + set_visibility_layer(visibility_layer | (1 << p_visibility_layer)); + } else { + set_visibility_layer(visibility_layer & (~(1 << p_visibility_layer))); + } +} + +bool CanvasItem::get_visibility_layer_bit(uint32_t p_visibility_layer) const { + ERR_FAIL_UNSIGNED_INDEX_V(p_visibility_layer, 32, false); + return (visibility_layer & (1 << p_visibility_layer)); +} + +void CanvasItem::_refresh_texture_filter_cache() { if (!is_inside_tree()) { return; } @@ -1107,6 +1140,14 @@ void CanvasItem::_update_texture_filter_changed(bool p_propagate) { } else { texture_filter_cache = RS::CanvasItemTextureFilter(texture_filter); } +} + +void CanvasItem::_update_texture_filter_changed(bool p_propagate) { + if (!is_inside_tree()) { + return; + } + _refresh_texture_filter_cache(); + RS::get_singleton()->canvas_item_set_default_texture_filter(get_canvas_item(), texture_filter_cache); queue_redraw(); @@ -1133,7 +1174,7 @@ CanvasItem::TextureFilter CanvasItem::get_texture_filter() const { return texture_filter; } -void CanvasItem::_update_texture_repeat_changed(bool p_propagate) { +void CanvasItem::_refresh_texture_repeat_cache() { if (!is_inside_tree()) { return; } @@ -1148,6 +1189,14 @@ void CanvasItem::_update_texture_repeat_changed(bool p_propagate) { } else { texture_repeat_cache = RS::CanvasItemTextureRepeat(texture_repeat); } +} + +void CanvasItem::_update_texture_repeat_changed(bool p_propagate) { + if (!is_inside_tree()) { + return; + } + _refresh_texture_repeat_cache(); + RS::get_singleton()->canvas_item_set_default_texture_repeat(get_canvas_item(), texture_repeat_cache); queue_redraw(); if (p_propagate) { @@ -1169,26 +1218,39 @@ void CanvasItem::set_texture_repeat(TextureRepeat p_texture_repeat) { notify_property_list_changed(); } -void CanvasItem::set_clip_children(bool p_enabled) { - if (clip_children == p_enabled) { +void CanvasItem::set_clip_children_mode(ClipChildrenMode p_clip_mode) { + ERR_FAIL_COND(p_clip_mode >= CLIP_CHILDREN_MAX); + + if (clip_children_mode == p_clip_mode) { return; } - clip_children = p_enabled; + clip_children_mode = p_clip_mode; if (Object::cast_to<CanvasGroup>(this) != nullptr) { //avoid accidental bugs, make this not work on CanvasGroup return; } - RS::get_singleton()->canvas_item_set_canvas_group_mode(get_canvas_item(), clip_children ? RS::CANVAS_GROUP_MODE_OPAQUE : RS::CANVAS_GROUP_MODE_DISABLED); + + RS::get_singleton()->canvas_item_set_canvas_group_mode(get_canvas_item(), RS::CanvasGroupMode(clip_children_mode)); } -bool CanvasItem::is_clipping_children() const { - return clip_children; +CanvasItem::ClipChildrenMode CanvasItem::get_clip_children_mode() const { + return clip_children_mode; } CanvasItem::TextureRepeat CanvasItem::get_texture_repeat() const { return texture_repeat; } +CanvasItem::TextureFilter CanvasItem::get_texture_filter_in_tree() { + _refresh_texture_filter_cache(); + return (TextureFilter)texture_filter_cache; +} + +CanvasItem::TextureRepeat CanvasItem::get_texture_repeat_in_tree() { + _refresh_texture_repeat_cache(); + return (TextureRepeat)texture_repeat_cache; +} + CanvasItem::CanvasItem() : xform_change(this) { canvas_item = RenderingServer::get_singleton()->canvas_item_create(); diff --git a/scene/main/canvas_item.h b/scene/main/canvas_item.h index b80289fdb4..4e78a175dc 100644 --- a/scene/main/canvas_item.h +++ b/scene/main/canvas_item.h @@ -66,8 +66,16 @@ public: TEXTURE_REPEAT_MAX, }; + enum ClipChildrenMode { + CLIP_CHILDREN_DISABLED, + CLIP_CHILDREN_ONLY, + CLIP_CHILDREN_AND_DRAW, + CLIP_CHILDREN_MAX, + }; + private: - mutable SelfList<Node> xform_change; + mutable SelfList<Node> + xform_change; RID canvas_item; StringName canvas_group; @@ -81,11 +89,11 @@ private: List<CanvasItem *>::Element *C = nullptr; int light_mask = 1; + uint32_t visibility_layer = 1; Window *window = nullptr; bool visible = true; bool parent_visible_in_tree = false; - bool clip_children = false; bool pending_update = false; bool top_level = false; bool drawing = false; @@ -95,6 +103,8 @@ private: bool notify_local_transform = false; bool notify_transform = false; + ClipChildrenMode clip_children_mode = CLIP_CHILDREN_DISABLED; + RS::CanvasItemTextureFilter texture_filter_cache = RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR; RS::CanvasItemTextureRepeat texture_repeat_cache = RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED; TextureFilter texture_filter = TEXTURE_FILTER_PARENT_NODE; @@ -121,7 +131,9 @@ private: static CanvasItem *current_item_drawn; friend class Viewport; + void _refresh_texture_repeat_cache(); void _update_texture_repeat_changed(bool p_propagate); + void _refresh_texture_filter_cache(); void _update_texture_filter_changed(bool p_propagate); protected: @@ -200,8 +212,8 @@ public: void queue_redraw(); void move_to_front(); - void set_clip_children(bool p_enabled); - bool is_clipping_children() const; + void set_clip_children_mode(ClipChildrenMode p_clip_mode); + ClipChildrenMode get_clip_children_mode() const; virtual void set_light_mask(int p_light_mask); int get_light_mask() const; @@ -212,6 +224,12 @@ public: void set_self_modulate(const Color &p_self_modulate); Color get_self_modulate() const; + void set_visibility_layer(uint32_t p_visibility_layer); + uint32_t get_visibility_layer() const; + + void set_visibility_layer_bit(uint32_t p_visibility_layer, bool p_enable); + bool get_visibility_layer_bit(uint32_t p_visibility_layer) const; + /* DRAWING API */ void draw_dashed_line(const Point2 &p_from, const Point2 &p_to, const Color &p_color, real_t p_width = 1.0, real_t p_dash = 2.0); @@ -310,6 +328,9 @@ public: virtual void set_texture_repeat(TextureRepeat p_texture_repeat); TextureRepeat get_texture_repeat() const; + TextureFilter get_texture_filter_in_tree(); + TextureRepeat get_texture_repeat_in_tree(); + // Used by control nodes to retrieve the parent's anchorable area virtual Rect2 get_anchorable_rect() const { return Rect2(0, 0, 0, 0); }; @@ -321,6 +342,7 @@ public: VARIANT_ENUM_CAST(CanvasItem::TextureFilter) VARIANT_ENUM_CAST(CanvasItem::TextureRepeat) +VARIANT_ENUM_CAST(CanvasItem::ClipChildrenMode) class CanvasTexture : public Texture2D { GDCLASS(CanvasTexture, Texture2D); diff --git a/scene/main/canvas_layer.cpp b/scene/main/canvas_layer.cpp index 214efe432b..5fde18721a 100644 --- a/scene/main/canvas_layer.cpp +++ b/scene/main/canvas_layer.cpp @@ -38,6 +38,7 @@ void CanvasLayer::set_layer(int p_xform) { layer = p_xform; if (viewport.is_valid()) { RenderingServer::get_singleton()->viewport_set_canvas_stacking(viewport, canvas, layer, get_index()); + vp->gui_set_root_order_dirty(); } } diff --git a/scene/main/http_request.cpp b/scene/main/http_request.cpp index bec378dd91..62f362553f 100644 --- a/scene/main/http_request.cpp +++ b/scene/main/http_request.cpp @@ -32,9 +32,6 @@ #include "core/io/compression.h" #include "scene/main/timer.h" -void HTTPRequest::_redirect_request(const String &p_new_url) { -} - Error HTTPRequest::_request() { return client->connect_to_host(url, port, use_tls, validate_tls); } @@ -48,6 +45,7 @@ Error HTTPRequest::_parse_url(const String &p_url) { body_len = -1; body.clear(); downloaded.set(0); + final_body_size.set(0); redirections = 0; String scheme; @@ -153,7 +151,7 @@ Error HTTPRequest::request_raw(const String &p_url, const Vector<String> &p_cust client->set_blocking_mode(false); err = _request(); if (err != OK) { - call_deferred(SNAME("_request_done"), RESULT_CANT_CONNECT, 0, PackedStringArray(), PackedByteArray()); + _defer_done(RESULT_CANT_CONNECT, 0, PackedStringArray(), PackedByteArray()); return ERR_CANT_CONNECT; } @@ -169,7 +167,7 @@ void HTTPRequest::_thread_func(void *p_userdata) { Error err = hr->_request(); if (err != OK) { - hr->call_deferred(SNAME("_request_done"), RESULT_CANT_CONNECT, 0, PackedStringArray(), PackedByteArray()); + hr->_defer_done(RESULT_CANT_CONNECT, 0, PackedStringArray(), PackedByteArray()); } else { while (!hr->thread_request_quit.is_set()) { bool exit = hr->_update_connection(); @@ -198,6 +196,7 @@ void HTTPRequest::cancel_request() { } file.unref(); + decompressor.unref(); client->close(); body.clear(); got_response = false; @@ -208,7 +207,7 @@ void HTTPRequest::cancel_request() { bool HTTPRequest::_handle_response(bool *ret_value) { if (!client->has_response()) { - call_deferred(SNAME("_request_done"), RESULT_NO_RESPONSE, 0, PackedStringArray(), PackedByteArray()); + _defer_done(RESULT_NO_RESPONSE, 0, PackedStringArray(), PackedByteArray()); *ret_value = true; return true; } @@ -219,6 +218,9 @@ bool HTTPRequest::_handle_response(bool *ret_value) { client->get_response_headers(&rheaders); response_headers.clear(); downloaded.set(0); + final_body_size.set(0); + decompressor.unref(); + for (const String &E : rheaders) { response_headers.push_back(E); } @@ -227,7 +229,7 @@ bool HTTPRequest::_handle_response(bool *ret_value) { // Handle redirect. if (max_redirects >= 0 && redirections >= max_redirects) { - call_deferred(SNAME("_request_done"), RESULT_REDIRECT_LIMIT_REACHED, response_code, response_headers, PackedByteArray()); + _defer_done(RESULT_REDIRECT_LIMIT_REACHED, response_code, response_headers, PackedByteArray()); *ret_value = true; return true; } @@ -259,6 +261,7 @@ bool HTTPRequest::_handle_response(bool *ret_value) { body_len = -1; body.clear(); downloaded.set(0); + final_body_size.set(0); redirections = new_redirs; *ret_value = false; return true; @@ -266,13 +269,26 @@ bool HTTPRequest::_handle_response(bool *ret_value) { } } + // Check if we need to start streaming decompression. + String content_encoding; + if (accept_gzip) { + content_encoding = get_header_value(response_headers, "Content-Encoding").to_lower(); + } + if (content_encoding == "gzip") { + decompressor.instantiate(); + decompressor->start_decompression(false, get_download_chunk_size()); + } else if (content_encoding == "deflate") { + decompressor.instantiate(); + decompressor->start_decompression(true, get_download_chunk_size()); + } + return false; } bool HTTPRequest::_update_connection() { switch (client->get_status()) { case HTTPClient::STATUS_DISCONNECTED: { - call_deferred(SNAME("_request_done"), RESULT_CANT_CONNECT, 0, PackedStringArray(), PackedByteArray()); + _defer_done(RESULT_CANT_CONNECT, 0, PackedStringArray(), PackedByteArray()); return true; // End it, since it's disconnected. } break; case HTTPClient::STATUS_RESOLVING: { @@ -281,7 +297,7 @@ bool HTTPRequest::_update_connection() { return false; } break; case HTTPClient::STATUS_CANT_RESOLVE: { - call_deferred(SNAME("_request_done"), RESULT_CANT_RESOLVE, 0, PackedStringArray(), PackedByteArray()); + _defer_done(RESULT_CANT_RESOLVE, 0, PackedStringArray(), PackedByteArray()); return true; } break; @@ -291,7 +307,7 @@ bool HTTPRequest::_update_connection() { return false; } break; // Connecting to IP. case HTTPClient::STATUS_CANT_CONNECT: { - call_deferred(SNAME("_request_done"), RESULT_CANT_CONNECT, 0, PackedStringArray(), PackedByteArray()); + _defer_done(RESULT_CANT_CONNECT, 0, PackedStringArray(), PackedByteArray()); return true; } break; @@ -306,16 +322,16 @@ bool HTTPRequest::_update_connection() { return ret_value; } - call_deferred(SNAME("_request_done"), RESULT_SUCCESS, response_code, response_headers, PackedByteArray()); + _defer_done(RESULT_SUCCESS, response_code, response_headers, PackedByteArray()); return true; } if (body_len < 0) { // Chunked transfer is done. - call_deferred(SNAME("_request_done"), RESULT_SUCCESS, response_code, response_headers, body); + _defer_done(RESULT_SUCCESS, response_code, response_headers, body); return true; } - call_deferred(SNAME("_request_done"), RESULT_CHUNKED_BODY_SIZE_MISMATCH, response_code, response_headers, PackedByteArray()); + _defer_done(RESULT_CHUNKED_BODY_SIZE_MISMATCH, response_code, response_headers, PackedByteArray()); return true; // Request might have been done. } else { @@ -324,7 +340,7 @@ bool HTTPRequest::_update_connection() { int size = request_data.size(); Error err = client->request(method, request_string, headers, size > 0 ? request_data.ptr() : nullptr, size); if (err != OK) { - call_deferred(SNAME("_request_done"), RESULT_CONNECTION_ERROR, 0, PackedStringArray(), PackedByteArray()); + _defer_done(RESULT_CONNECTION_ERROR, 0, PackedStringArray(), PackedByteArray()); return true; } @@ -347,7 +363,7 @@ bool HTTPRequest::_update_connection() { } if (!client->is_response_chunked() && client->get_response_body_length() == 0) { - call_deferred(SNAME("_request_done"), RESULT_SUCCESS, response_code, response_headers, PackedByteArray()); + _defer_done(RESULT_SUCCESS, response_code, response_headers, PackedByteArray()); return true; } @@ -356,14 +372,14 @@ bool HTTPRequest::_update_connection() { body_len = client->get_response_body_length(); if (body_size_limit >= 0 && body_len > body_size_limit) { - call_deferred(SNAME("_request_done"), RESULT_BODY_SIZE_LIMIT_EXCEEDED, response_code, response_headers, PackedByteArray()); + _defer_done(RESULT_BODY_SIZE_LIMIT_EXCEEDED, response_code, response_headers, PackedByteArray()); return true; } if (!download_to_file.is_empty()) { file = FileAccess::open(download_to_file, FileAccess::WRITE); if (file.is_null()) { - call_deferred(SNAME("_request_done"), RESULT_DOWNLOAD_FILE_CANT_OPEN, response_code, response_headers, PackedByteArray()); + _defer_done(RESULT_DOWNLOAD_FILE_CANT_OPEN, response_code, response_headers, PackedByteArray()); return true; } } @@ -374,15 +390,53 @@ bool HTTPRequest::_update_connection() { return false; } - PackedByteArray chunk = client->read_response_body_chunk(); + PackedByteArray chunk; + if (decompressor.is_null()) { + // Chunk can be read directly. + chunk = client->read_response_body_chunk(); + downloaded.add(chunk.size()); + } else { + // Chunk is the result of decompression. + PackedByteArray compressed = client->read_response_body_chunk(); + downloaded.add(compressed.size()); + + int pos = 0; + int left = compressed.size(); + while (left) { + int w = 0; + Error err = decompressor->put_partial_data(compressed.ptr() + pos, left, w); + if (err == OK) { + PackedByteArray dc; + dc.resize(decompressor->get_available_bytes()); + err = decompressor->get_data(dc.ptrw(), dc.size()); + chunk.append_array(dc); + } + if (err != OK) { + _defer_done(RESULT_BODY_DECOMPRESS_FAILED, response_code, response_headers, PackedByteArray()); + return true; + } + // We need this check here because a "zip bomb" could result in a chunk of few kilos decompressing into gigabytes of data. + if (body_size_limit >= 0 && final_body_size.get() + chunk.size() > body_size_limit) { + _defer_done(RESULT_BODY_SIZE_LIMIT_EXCEEDED, response_code, response_headers, PackedByteArray()); + return true; + } + pos += w; + left -= w; + } + } + final_body_size.add(chunk.size()); + + if (body_size_limit >= 0 && final_body_size.get() > body_size_limit) { + _defer_done(RESULT_BODY_SIZE_LIMIT_EXCEEDED, response_code, response_headers, PackedByteArray()); + return true; + } if (chunk.size()) { - downloaded.add(chunk.size()); if (file.is_valid()) { const uint8_t *r = chunk.ptr(); file->store_buffer(r, chunk.size()); if (file->get_error() != OK) { - call_deferred(SNAME("_request_done"), RESULT_DOWNLOAD_FILE_WRITE_ERROR, response_code, response_headers, PackedByteArray()); + _defer_done(RESULT_DOWNLOAD_FILE_WRITE_ERROR, response_code, response_headers, PackedByteArray()); return true; } } else { @@ -390,19 +444,14 @@ bool HTTPRequest::_update_connection() { } } - if (body_size_limit >= 0 && downloaded.get() > body_size_limit) { - call_deferred(SNAME("_request_done"), RESULT_BODY_SIZE_LIMIT_EXCEEDED, response_code, response_headers, PackedByteArray()); - return true; - } - if (body_len >= 0) { if (downloaded.get() == body_len) { - call_deferred(SNAME("_request_done"), RESULT_SUCCESS, response_code, response_headers, body); + _defer_done(RESULT_SUCCESS, response_code, response_headers, body); return true; } } else if (client->get_status() == HTTPClient::STATUS_DISCONNECTED) { // We read till EOF, with no errors. Request is done. - call_deferred(SNAME("_request_done"), RESULT_SUCCESS, response_code, response_headers, body); + _defer_done(RESULT_SUCCESS, response_code, response_headers, body); return true; } @@ -410,11 +459,11 @@ bool HTTPRequest::_update_connection() { } break; // Request resulted in body: break which must be read. case HTTPClient::STATUS_CONNECTION_ERROR: { - call_deferred(SNAME("_request_done"), RESULT_CONNECTION_ERROR, 0, PackedStringArray(), PackedByteArray()); + _defer_done(RESULT_CONNECTION_ERROR, 0, PackedStringArray(), PackedByteArray()); return true; } break; case HTTPClient::STATUS_TLS_HANDSHAKE_ERROR: { - call_deferred(SNAME("_request_done"), RESULT_TLS_HANDSHAKE_ERROR, 0, PackedStringArray(), PackedByteArray()); + _defer_done(RESULT_TLS_HANDSHAKE_ERROR, 0, PackedStringArray(), PackedByteArray()); return true; } break; } @@ -422,41 +471,13 @@ bool HTTPRequest::_update_connection() { ERR_FAIL_V(false); } +void HTTPRequest::_defer_done(int p_status, int p_code, const PackedStringArray &p_headers, const PackedByteArray &p_data) { + call_deferred(SNAME("_request_done"), p_status, p_code, p_headers, p_data); +} + void HTTPRequest::_request_done(int p_status, int p_code, const PackedStringArray &p_headers, const PackedByteArray &p_data) { cancel_request(); - // Determine if the request body is compressed. - bool is_compressed; - String content_encoding = get_header_value(p_headers, "Content-Encoding").to_lower(); - Compression::Mode mode; - if (content_encoding == "gzip") { - mode = Compression::Mode::MODE_GZIP; - is_compressed = true; - } else if (content_encoding == "deflate") { - mode = Compression::Mode::MODE_DEFLATE; - is_compressed = true; - } else { - is_compressed = false; - } - - if (accept_gzip && is_compressed && p_data.size() > 0) { - // Decompress request body - PackedByteArray decompressed; - int result = Compression::decompress_dynamic(&decompressed, body_size_limit, p_data.ptr(), p_data.size(), mode); - if (result == OK) { - emit_signal(SNAME("request_completed"), p_status, p_code, p_headers, decompressed); - return; - } else if (result == -5) { - WARN_PRINT("Decompressed size of HTTP response body exceeded body_size_limit"); - p_status = RESULT_BODY_SIZE_LIMIT_EXCEEDED; - // Just return the raw data if we failed to decompress it. - } else { - WARN_PRINT("Failed to decompress HTTP response body"); - p_status = RESULT_BODY_DECOMPRESS_FAILED; - // Just return the raw data if we failed to decompress it. - } - } - emit_signal(SNAME("request_completed"), p_status, p_code, p_headers, p_data); } @@ -566,7 +587,7 @@ double HTTPRequest::get_timeout() { void HTTPRequest::_timeout() { cancel_request(); - call_deferred(SNAME("_request_done"), RESULT_TIMEOUT, 0, PackedStringArray(), PackedByteArray()); + _defer_done(RESULT_TIMEOUT, 0, PackedStringArray(), PackedByteArray()); } void HTTPRequest::_bind_methods() { @@ -594,7 +615,6 @@ void HTTPRequest::_bind_methods() { ClassDB::bind_method(D_METHOD("get_downloaded_bytes"), &HTTPRequest::get_downloaded_bytes); ClassDB::bind_method(D_METHOD("get_body_size"), &HTTPRequest::get_body_size); - ClassDB::bind_method(D_METHOD("_redirect_request"), &HTTPRequest::_redirect_request); ClassDB::bind_method(D_METHOD("_request_done"), &HTTPRequest::_request_done); ClassDB::bind_method(D_METHOD("set_timeout", "timeout"), &HTTPRequest::set_timeout); diff --git a/scene/main/http_request.h b/scene/main/http_request.h index 290bacd9d2..80445684b0 100644 --- a/scene/main/http_request.h +++ b/scene/main/http_request.h @@ -32,6 +32,7 @@ #define HTTP_REQUEST_H #include "core/io/http_client.h" +#include "core/io/stream_peer_gzip.h" #include "core/os/thread.h" #include "core/templates/safe_refcount.h" #include "scene/main/node.h" @@ -84,10 +85,12 @@ private: String download_to_file; + Ref<StreamPeerGZIP> decompressor; Ref<FileAccess> file; int body_len = -1; SafeNumeric<int> downloaded; + SafeNumeric<int> final_body_size; int body_size_limit = -1; int redirections = 0; @@ -113,6 +116,7 @@ private: Thread thread; + void _defer_done(int p_status, int p_code, const PackedStringArray &p_headers, const PackedByteArray &p_data); void _request_done(int p_status, int p_code, const PackedStringArray &p_headers, const PackedByteArray &p_data); static void _thread_func(void *p_userdata); diff --git a/scene/main/instance_placeholder.cpp b/scene/main/instance_placeholder.cpp index 6dd83e4636..56e719968b 100644 --- a/scene/main/instance_placeholder.cpp +++ b/scene/main/instance_placeholder.cpp @@ -100,7 +100,7 @@ Node *InstancePlaceholder::create_instance(bool p_replace, const Ref<PackedScene } if (p_replace) { - queue_delete(); + queue_free(); base->remove_child(this); } diff --git a/scene/main/missing_node.cpp b/scene/main/missing_node.cpp index 395fdad9e4..7ce527fd9c 100644 --- a/scene/main/missing_node.cpp +++ b/scene/main/missing_node.cpp @@ -74,9 +74,9 @@ bool MissingNode::is_recording_properties() const { return recording_properties; } -TypedArray<String> MissingNode::get_configuration_warnings() const { +PackedStringArray MissingNode::get_configuration_warnings() const { // The mere existence of this node is warning. - TypedArray<String> ret; + PackedStringArray ret; ret.push_back(vformat(RTR("This node was saved as class type '%s', which was no longer available when this scene was loaded."), original_class)); ret.push_back(RTR("Data from the original node is kept as a placeholder until this type of node is available again. It can hence be safely re-saved without risk of data loss.")); return ret; diff --git a/scene/main/missing_node.h b/scene/main/missing_node.h index d200fbb47f..0003f71f29 100644 --- a/scene/main/missing_node.h +++ b/scene/main/missing_node.h @@ -55,7 +55,7 @@ public: void set_recording_properties(bool p_enable); bool is_recording_properties() const; - virtual TypedArray<String> get_configuration_warnings() const override; + virtual PackedStringArray get_configuration_warnings() const override; MissingNode(); }; diff --git a/scene/main/multiplayer_api.cpp b/scene/main/multiplayer_api.cpp index 2d2103f031..8b4b98c172 100644 --- a/scene/main/multiplayer_api.cpp +++ b/scene/main/multiplayer_api.cpp @@ -39,7 +39,7 @@ #include "core/os/os.h" #endif -StringName MultiplayerAPI::default_interface = StringName(); +StringName MultiplayerAPI::default_interface; void MultiplayerAPI::set_default_interface(const StringName &p_interface) { ERR_FAIL_COND_MSG(!ClassDB::is_parent_class(p_interface, MultiplayerAPI::get_class_static()), vformat("Can't make %s the default multiplayer interface since it does not extend MultiplayerAPI.", p_interface)); @@ -329,11 +329,9 @@ void MultiplayerAPI::_bind_methods() { /// MultiplayerAPIExtension Error MultiplayerAPIExtension::poll() { - int err; - if (GDVIRTUAL_CALL(_poll, err)) { - return (Error)err; - } - return OK; + int err = OK; + GDVIRTUAL_CALL(_poll, err); + return (Error)err; } void MultiplayerAPIExtension::set_multiplayer_peer(const Ref<MultiplayerPeer> &p_peer) { @@ -342,26 +340,20 @@ void MultiplayerAPIExtension::set_multiplayer_peer(const Ref<MultiplayerPeer> &p Ref<MultiplayerPeer> MultiplayerAPIExtension::get_multiplayer_peer() { Ref<MultiplayerPeer> peer; - if (GDVIRTUAL_CALL(_get_multiplayer_peer, peer)) { - return peer; - } - return nullptr; + GDVIRTUAL_CALL(_get_multiplayer_peer, peer); + return peer; } int MultiplayerAPIExtension::get_unique_id() { - int id; - if (GDVIRTUAL_CALL(_get_unique_id, id)) { - return id; - } - return 1; + int id = 1; + GDVIRTUAL_CALL(_get_unique_id, id); + return id; } Vector<int> MultiplayerAPIExtension::get_peer_ids() { Vector<int> ids; - if (GDVIRTUAL_CALL(_get_peer_ids, ids)) { - return ids; - } - return Vector<int>(); + GDVIRTUAL_CALL(_get_peer_ids, ids); + return ids; } Error MultiplayerAPIExtension::rpcp(Object *p_obj, int p_peer_id, const StringName &p_method, const Variant **p_arg, int p_argcount) { @@ -372,35 +364,27 @@ Error MultiplayerAPIExtension::rpcp(Object *p_obj, int p_peer_id, const StringNa for (int i = 0; i < p_argcount; i++) { args.push_back(*p_arg[i]); } - int ret; - if (GDVIRTUAL_CALL(_rpc, p_peer_id, p_obj, p_method, args, ret)) { - return (Error)ret; - } - return FAILED; + int ret = FAILED; + GDVIRTUAL_CALL(_rpc, p_peer_id, p_obj, p_method, args, ret); + return (Error)ret; } int MultiplayerAPIExtension::get_remote_sender_id() { - int id; - if (GDVIRTUAL_CALL(_get_remote_sender_id, id)) { - return id; - } - return 0; + int id = 0; + GDVIRTUAL_CALL(_get_remote_sender_id, id); + return id; } Error MultiplayerAPIExtension::object_configuration_add(Object *p_object, Variant p_config) { - int err; - if (GDVIRTUAL_CALL(_object_configuration_add, p_object, p_config, err)) { - return (Error)err; - } - return ERR_UNAVAILABLE; + int err = ERR_UNAVAILABLE; + GDVIRTUAL_CALL(_object_configuration_add, p_object, p_config, err); + return (Error)err; } Error MultiplayerAPIExtension::object_configuration_remove(Object *p_object, Variant p_config) { - int err; - if (GDVIRTUAL_CALL(_object_configuration_remove, p_object, p_config, err)) { - return (Error)err; - } - return ERR_UNAVAILABLE; + int err = ERR_UNAVAILABLE; + GDVIRTUAL_CALL(_object_configuration_remove, p_object, p_config, err); + return (Error)err; } void MultiplayerAPIExtension::_bind_methods() { diff --git a/scene/main/multiplayer_peer.cpp b/scene/main/multiplayer_peer.cpp index 9b63118f7c..92ba3debd1 100644 --- a/scene/main/multiplayer_peer.cpp +++ b/scene/main/multiplayer_peer.cpp @@ -78,6 +78,10 @@ bool MultiplayerPeer::is_refusing_new_connections() const { return refuse_connections; } +bool MultiplayerPeer::is_server_relay_supported() const { + return false; +} + void MultiplayerPeer::_bind_methods() { ClassDB::bind_method(D_METHOD("set_transfer_channel", "channel"), &MultiplayerPeer::set_transfer_channel); ClassDB::bind_method(D_METHOD("get_transfer_channel"), &MultiplayerPeer::get_transfer_channel); @@ -86,8 +90,12 @@ void MultiplayerPeer::_bind_methods() { ClassDB::bind_method(D_METHOD("set_target_peer", "id"), &MultiplayerPeer::set_target_peer); ClassDB::bind_method(D_METHOD("get_packet_peer"), &MultiplayerPeer::get_packet_peer); + ClassDB::bind_method(D_METHOD("get_packet_channel"), &MultiplayerPeer::get_packet_channel); + ClassDB::bind_method(D_METHOD("get_packet_mode"), &MultiplayerPeer::get_packet_mode); ClassDB::bind_method(D_METHOD("poll"), &MultiplayerPeer::poll); + ClassDB::bind_method(D_METHOD("close"), &MultiplayerPeer::close); + ClassDB::bind_method(D_METHOD("disconnect_peer", "peer", "force"), &MultiplayerPeer::disconnect_peer, DEFVAL(false)); ClassDB::bind_method(D_METHOD("get_connection_status"), &MultiplayerPeer::get_connection_status); ClassDB::bind_method(D_METHOD("get_unique_id"), &MultiplayerPeer::get_unique_id); @@ -96,6 +104,8 @@ void MultiplayerPeer::_bind_methods() { ClassDB::bind_method(D_METHOD("set_refuse_new_connections", "enable"), &MultiplayerPeer::set_refuse_new_connections); ClassDB::bind_method(D_METHOD("is_refusing_new_connections"), &MultiplayerPeer::is_refusing_new_connections); + ClassDB::bind_method(D_METHOD("is_server_relay_supported"), &MultiplayerPeer::is_server_relay_supported); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "refuse_new_connections"), "set_refuse_new_connections", "is_refusing_new_connections"); ADD_PROPERTY(PropertyInfo(Variant::INT, "transfer_mode", PROPERTY_HINT_ENUM, "Unreliable,Unreliable Ordered,Reliable"), "set_transfer_mode", "get_transfer_mode"); ADD_PROPERTY(PropertyInfo(Variant::INT, "transfer_channel", PROPERTY_HINT_RANGE, "0,255,1"), "set_transfer_channel", "get_transfer_channel"); @@ -113,9 +123,6 @@ void MultiplayerPeer::_bind_methods() { ADD_SIGNAL(MethodInfo("peer_connected", PropertyInfo(Variant::INT, "id"))); ADD_SIGNAL(MethodInfo("peer_disconnected", PropertyInfo(Variant::INT, "id"))); - ADD_SIGNAL(MethodInfo("server_disconnected")); - ADD_SIGNAL(MethodInfo("connection_succeeded")); - ADD_SIGNAL(MethodInfo("connection_failed")); } /*************/ @@ -177,6 +184,14 @@ bool MultiplayerPeerExtension::is_refusing_new_connections() const { return MultiplayerPeer::is_refusing_new_connections(); } +bool MultiplayerPeerExtension::is_server_relay_supported() const { + bool can_relay; + if (GDVIRTUAL_CALL(_is_server_relay_supported, can_relay)) { + return can_relay; + } + return MultiplayerPeer::is_server_relay_supported(); +} + void MultiplayerPeerExtension::_bind_methods() { GDVIRTUAL_BIND(_get_packet, "r_buffer", "r_buffer_size"); GDVIRTUAL_BIND(_put_packet, "p_buffer", "p_buffer_size"); @@ -197,6 +212,8 @@ void MultiplayerPeerExtension::_bind_methods() { GDVIRTUAL_BIND(_get_packet_peer); GDVIRTUAL_BIND(_is_server); GDVIRTUAL_BIND(_poll); + GDVIRTUAL_BIND(_close); + GDVIRTUAL_BIND(_disconnect_peer, "p_peer", "p_force"); GDVIRTUAL_BIND(_get_unique_id); GDVIRTUAL_BIND(_set_refuse_new_connections, "p_enable"); GDVIRTUAL_BIND(_is_refusing_new_connections); diff --git a/scene/main/multiplayer_peer.h b/scene/main/multiplayer_peer.h index ab7483ece5..4b5909538e 100644 --- a/scene/main/multiplayer_peer.h +++ b/scene/main/multiplayer_peer.h @@ -74,14 +74,20 @@ public: virtual TransferMode get_transfer_mode() const; virtual void set_refuse_new_connections(bool p_enable); virtual bool is_refusing_new_connections() const; + virtual bool is_server_relay_supported() const; virtual void set_target_peer(int p_peer_id) = 0; virtual int get_packet_peer() const = 0; + virtual TransferMode get_packet_mode() const = 0; + virtual int get_packet_channel() const = 0; + + virtual void disconnect_peer(int p_peer, bool p_force = false) = 0; virtual bool is_server() const = 0; virtual void poll() = 0; + virtual void close() = 0; virtual int get_unique_id() const = 0; @@ -123,14 +129,21 @@ public: virtual bool is_refusing_new_connections() const override; GDVIRTUAL0RC(bool, _is_refusing_new_connections); // Optional. + virtual bool is_server_relay_supported() const override; + GDVIRTUAL0RC(bool, _is_server_relay_supported); // Optional. + EXBIND1(set_transfer_channel, int); EXBIND0RC(int, get_transfer_channel); EXBIND1(set_transfer_mode, TransferMode); EXBIND0RC(TransferMode, get_transfer_mode); EXBIND1(set_target_peer, int); EXBIND0RC(int, get_packet_peer); + EXBIND0RC(TransferMode, get_packet_mode); + EXBIND0RC(int, get_packet_channel); EXBIND0RC(bool, is_server); EXBIND0(poll); + EXBIND0(close); + EXBIND2(disconnect_peer, int, bool); EXBIND0RC(int, get_unique_id); EXBIND0RC(ConnectionStatus, get_connection_status); }; diff --git a/scene/main/node.cpp b/scene/main/node.cpp index 29f4d4fb1c..680c4cd7e4 100644 --- a/scene/main/node.cpp +++ b/scene/main/node.cpp @@ -322,62 +322,62 @@ void Node::_propagate_exit_tree() { data.depth = -1; } -void Node::move_child(Node *p_child, int p_pos) { +void Node::move_child(Node *p_child, int p_index) { ERR_FAIL_NULL(p_child); ERR_FAIL_COND_MSG(p_child->data.parent != this, "Child is not a child of this node."); // We need to check whether node is internal and move it only in the relevant node range. if (p_child->_is_internal_front()) { - if (p_pos < 0) { - p_pos += data.internal_children_front; + if (p_index < 0) { + p_index += data.internal_children_front; } - ERR_FAIL_INDEX_MSG(p_pos, data.internal_children_front, vformat("Invalid new child position: %d. Child is internal.", p_pos)); - _move_child(p_child, p_pos); + ERR_FAIL_INDEX_MSG(p_index, data.internal_children_front, vformat("Invalid new child index: %d. Child is internal.", p_index)); + _move_child(p_child, p_index); } else if (p_child->_is_internal_back()) { - if (p_pos < 0) { - p_pos += data.internal_children_back; + if (p_index < 0) { + p_index += data.internal_children_back; } - ERR_FAIL_INDEX_MSG(p_pos, data.internal_children_back, vformat("Invalid new child position: %d. Child is internal.", p_pos)); - _move_child(p_child, data.children.size() - data.internal_children_back + p_pos); + ERR_FAIL_INDEX_MSG(p_index, data.internal_children_back, vformat("Invalid new child index: %d. Child is internal.", p_index)); + _move_child(p_child, data.children.size() - data.internal_children_back + p_index); } else { - if (p_pos < 0) { - p_pos += get_child_count(false); + if (p_index < 0) { + p_index += get_child_count(false); } - ERR_FAIL_INDEX_MSG(p_pos, data.children.size() + 1 - data.internal_children_front - data.internal_children_back, vformat("Invalid new child position: %d.", p_pos)); - _move_child(p_child, p_pos + data.internal_children_front); + ERR_FAIL_INDEX_MSG(p_index, data.children.size() + 1 - data.internal_children_front - data.internal_children_back, vformat("Invalid new child index: %d.", p_index)); + _move_child(p_child, p_index + data.internal_children_front); } } -void Node::_move_child(Node *p_child, int p_pos, bool p_ignore_end) { - ERR_FAIL_COND_MSG(data.blocked > 0, "Parent node is busy setting up children, move_child() failed. Consider using call_deferred(\"move_child\") instead (or \"popup\" if this is from a popup)."); +void Node::_move_child(Node *p_child, int p_index, bool p_ignore_end) { + ERR_FAIL_COND_MSG(data.blocked > 0, "Parent node is busy setting up children, `move_child()` failed. Consider using `move_child.call_deferred(child, index)` instead (or `popup.call_deferred()` if this is from a popup)."); // Specifying one place beyond the end - // means the same as moving to the last position + // means the same as moving to the last index if (!p_ignore_end) { // p_ignore_end is a little hack to make back internal children work properly. if (p_child->_is_internal_front()) { - if (p_pos == data.internal_children_front) { - p_pos--; + if (p_index == data.internal_children_front) { + p_index--; } } else if (p_child->_is_internal_back()) { - if (p_pos == data.children.size()) { - p_pos--; + if (p_index == data.children.size()) { + p_index--; } } else { - if (p_pos == data.children.size() - data.internal_children_back) { - p_pos--; + if (p_index == data.children.size() - data.internal_children_back) { + p_index--; } } } - if (p_child->data.pos == p_pos) { + if (p_child->data.index == p_index) { return; //do nothing } - int motion_from = MIN(p_pos, p_child->data.pos); - int motion_to = MAX(p_pos, p_child->data.pos); + int motion_from = MIN(p_index, p_child->data.index); + int motion_to = MAX(p_index, p_child->data.index); - data.children.remove_at(p_child->data.pos); - data.children.insert(p_pos, p_child); + data.children.remove_at(p_child->data.index); + data.children.insert(p_index, p_child); if (data.tree) { data.tree->tree_changed(); @@ -386,7 +386,7 @@ void Node::_move_child(Node *p_child, int p_pos, bool p_ignore_end) { data.blocked++; //new pos first for (int i = motion_from; i <= motion_to; i++) { - data.children[i]->data.pos = i; + data.children[i]->data.index = i; } // notification second move_child_notify(p_child); @@ -1024,11 +1024,9 @@ String increase_numeric_string(const String &s) { void Node::_generate_serial_child_name(const Node *p_child, StringName &name) const { if (name == StringName()) { - //no name and a new name is needed, create one. + // No name and a new name is needed, create one. name = p_child->get_class(); - // Adjust casing according to project setting. - name = adjust_name_casing(name); } //quickly test if proposed name exists @@ -1106,7 +1104,7 @@ void Node::_add_child_nocheck(Node *p_child, const StringName &p_name) { //add a child node quickly, without name validation p_child->data.name = p_name; - p_child->data.pos = data.children.size(); + p_child->data.index = data.children.size(); data.children.push_back(p_child); p_child->data.parent = this; @@ -1132,7 +1130,7 @@ void Node::add_child(Node *p_child, bool p_force_readable_name, InternalMode p_i #ifdef DEBUG_ENABLED ERR_FAIL_COND_MSG(p_child->is_ancestor_of(this), vformat("Can't add child '%s' to '%s' as it would result in a cyclic dependency since '%s' is already a parent of '%s'.", p_child->get_name(), get_name(), p_child->get_name(), get_name())); #endif - ERR_FAIL_COND_MSG(data.blocked > 0, "Parent node is busy setting up children, add_node() failed. Consider using call_deferred(\"add_child\", child) instead."); + ERR_FAIL_COND_MSG(data.blocked > 0, "Parent node is busy setting up children, `add_child()` failed. Consider using `add_child.call_deferred(child)` instead."); _validate_child_name(p_child, p_force_readable_name); _add_child_nocheck(p_child, p_child->data.name); @@ -1152,7 +1150,7 @@ void Node::add_sibling(Node *p_sibling, bool p_force_readable_name) { ERR_FAIL_NULL(p_sibling); ERR_FAIL_NULL(data.parent); ERR_FAIL_COND_MSG(p_sibling == this, vformat("Can't add sibling '%s' to itself.", p_sibling->get_name())); // adding to itself! - ERR_FAIL_COND_MSG(data.blocked > 0, "Parent node is busy setting up children, add_sibling() failed. Consider using call_deferred(\"add_sibling\", sibling) instead."); + ERR_FAIL_COND_MSG(data.blocked > 0, "Parent node is busy setting up children, `add_sibling()` failed. Consider using `add_sibling.call_deferred(sibling)` instead."); InternalMode internal = INTERNAL_MODE_DISABLED; if (_is_internal_front()) { // The sibling will have the same internal status. @@ -1167,15 +1165,15 @@ void Node::add_sibling(Node *p_sibling, bool p_force_readable_name) { void Node::remove_child(Node *p_child) { ERR_FAIL_NULL(p_child); - ERR_FAIL_COND_MSG(data.blocked > 0, "Parent node is busy setting up children, remove_node() failed. Consider using call_deferred(\"remove_child\", child) instead."); + ERR_FAIL_COND_MSG(data.blocked > 0, "Parent node is busy setting up children, `remove_child()` failed. Consider using `remove_child.call_deferred(child)` instead."); int child_count = data.children.size(); Node **children = data.children.ptrw(); int idx = -1; - if (p_child->data.pos >= 0 && p_child->data.pos < child_count) { - if (children[p_child->data.pos] == p_child) { - idx = p_child->data.pos; + if (p_child->data.index >= 0 && p_child->data.index < child_count) { + if (children[p_child->data.index] == p_child) { + idx = p_child->data.index; } } @@ -1211,12 +1209,12 @@ void Node::remove_child(Node *p_child) { children = data.children.ptrw(); for (int i = idx; i < child_count; i++) { - children[i]->data.pos = i; + children[i]->data.index = i; children[i]->notification(NOTIFICATION_MOVED_IN_PARENT); } p_child->data.parent = nullptr; - p_child->data.pos = -1; + p_child->data.index = -1; if (data.inside_tree) { p_child->_propagate_after_exit_tree(); @@ -1343,12 +1341,23 @@ Node *Node::get_node(const NodePath &p_path) const { Node *node = get_node_or_null(p_path); if (unlikely(!node)) { + // Try to get a clear description of this node in the error message. + String desc; + if (is_inside_tree()) { + desc = get_path(); + } else { + desc = get_name(); + if (desc.is_empty()) { + desc = get_class(); + } + } + if (p_path.is_absolute()) { ERR_FAIL_V_MSG(nullptr, - vformat(R"(Node not found: "%s" (absolute path attempted from "%s").)", p_path, get_path())); + vformat(R"(Node not found: "%s" (absolute path attempted from "%s").)", p_path, desc)); } else { ERR_FAIL_V_MSG(nullptr, - vformat(R"(Node not found: "%s" (relative to "%s").)", p_path, get_path())); + vformat(R"(Node not found: "%s" (relative to "%s").)", p_path, desc)); } } @@ -1411,14 +1420,14 @@ TypedArray<Node> Node::find_children(const String &p_pattern, const String &p_ty if (cptr[i]->is_class(p_type)) { ret.append(cptr[i]); } else if (cptr[i]->get_script_instance()) { - Ref<Script> script = cptr[i]->get_script_instance()->get_script(); - while (script.is_valid()) { - if ((ScriptServer::is_global_class(p_type) && ScriptServer::get_global_class_path(p_type) == script->get_path()) || p_type == script->get_path()) { + Ref<Script> scr = cptr[i]->get_script_instance()->get_script(); + while (scr.is_valid()) { + if ((ScriptServer::is_global_class(p_type) && ScriptServer::get_global_class_path(p_type) == scr->get_path()) || p_type == scr->get_path()) { ret.append(cptr[i]); break; } - script = script->get_base_script(); + scr = scr->get_base_script(); } } @@ -1466,26 +1475,16 @@ bool Node::is_greater_than(const Node *p_node) const { ERR_FAIL_COND_V(data.depth < 0, false); ERR_FAIL_COND_V(p_node->data.depth < 0, false); -#ifdef NO_ALLOCA - - Vector<int> this_stack; - Vector<int> that_stack; - this_stack.resize(data.depth); - that_stack.resize(p_node->data.depth); - -#else int *this_stack = (int *)alloca(sizeof(int) * data.depth); int *that_stack = (int *)alloca(sizeof(int) * p_node->data.depth); -#endif - const Node *n = this; int idx = data.depth - 1; while (n) { ERR_FAIL_INDEX_V(idx, data.depth, false); - this_stack[idx--] = n->data.pos; + this_stack[idx--] = n->data.index; n = n->data.parent; } ERR_FAIL_COND_V(idx != -1, false); @@ -1493,7 +1492,7 @@ bool Node::is_greater_than(const Node *p_node) const { idx = p_node->data.depth - 1; while (n) { ERR_FAIL_INDEX_V(idx, p_node->data.depth, false); - that_stack[idx--] = n->data.pos; + that_stack[idx--] = n->data.index; n = n->data.parent; } @@ -1661,7 +1660,7 @@ Node *Node::find_common_parent_with(const Node *p_node) const { return const_cast<Node *>(common_parent); } -NodePath Node::get_path_to(const Node *p_node) const { +NodePath Node::get_path_to(const Node *p_node, bool p_use_unique_path) const { ERR_FAIL_NULL_V(p_node, NodePath()); if (this == p_node) { @@ -1691,20 +1690,58 @@ NodePath Node::get_path_to(const Node *p_node) const { visited.clear(); Vector<StringName> path; + StringName up = String(".."); - n = p_node; + if (p_use_unique_path) { + n = p_node; - while (n != common_parent) { - path.push_back(n->get_name()); - n = n->data.parent; - } + bool is_detected = false; + while (n != common_parent) { + if (n->is_unique_name_in_owner() && n->get_owner() == get_owner()) { + path.push_back(UNIQUE_NODE_PREFIX + String(n->get_name())); + is_detected = true; + break; + } + path.push_back(n->get_name()); + n = n->data.parent; + } - n = this; - StringName up = String(".."); + if (!is_detected) { + n = this; - while (n != common_parent) { - path.push_back(up); - n = n->data.parent; + String detected_name; + int up_count = 0; + while (n != common_parent) { + if (n->is_unique_name_in_owner() && n->get_owner() == get_owner()) { + detected_name = n->get_name(); + up_count = 0; + } + up_count++; + n = n->data.parent; + } + + for (int i = 0; i < up_count; i++) { + path.push_back(up); + } + + if (!detected_name.is_empty()) { + path.push_back(UNIQUE_NODE_PREFIX + detected_name); + } + } + } else { + n = p_node; + + while (n != common_parent) { + path.push_back(n->get_name()); + n = n->data.parent; + } + + n = this; + + while (n != common_parent) { + path.push_back(up); + n = n->data.parent; + } } path.reverse(); @@ -1760,11 +1797,11 @@ void Node::add_to_group(const StringName &p_identifier, bool p_persistent) { } void Node::remove_from_group(const StringName &p_identifier) { - ERR_FAIL_COND(!data.grouped.has(p_identifier)); - HashMap<StringName, GroupData>::Iterator E = data.grouped.find(p_identifier); - ERR_FAIL_COND(!E); + if (!E) { + return; + } if (data.tree) { data.tree->remove_from_group(E->key, this); @@ -1904,9 +1941,9 @@ int Node::get_index(bool p_include_internal) const { ERR_FAIL_COND_V_MSG(!p_include_internal && (_is_internal_front() || _is_internal_back()), -1, "Node is internal. Can't get index with 'include_internal' being false."); if (data.parent && !p_include_internal) { - return data.pos - data.parent->data.internal_children_front; + return data.index - data.parent->data.internal_children_front; } - return data.pos; + return data.index; } Ref<Tween> Node::create_tween() { @@ -2061,7 +2098,7 @@ Node *Node::_duplicate(int p_flags, HashMap<const Node *, Node *> *r_duplimap) c nip->set_instance_path(ip->get_instance_path()); node = nip; - } else if ((p_flags & DUPLICATE_USE_INSTANCING) && !get_scene_file_path().is_empty()) { + } else if ((p_flags & DUPLICATE_USE_INSTANTIATION) && !get_scene_file_path().is_empty()) { Ref<PackedScene> res = ResourceLoader::load(get_scene_file_path()); ERR_FAIL_COND_V(res.is_null(), nullptr); PackedScene::GenEditState ges = PackedScene::GEN_EDIT_STATE_DISABLED; @@ -2131,9 +2168,9 @@ Node *Node::_duplicate(int p_flags, HashMap<const Node *, Node *> *r_duplimap) c if (p_flags & DUPLICATE_SCRIPTS) { bool is_valid = false; - Variant script = N->get()->get(script_property_name, &is_valid); + Variant scr = N->get()->get(script_property_name, &is_valid); if (is_valid) { - current_node->set(script_property_name, script); + current_node->set(script_property_name, scr); } } @@ -2247,7 +2284,7 @@ Node *Node::duplicate_from_editor(HashMap<const Node *, Node *> &r_duplimap) con } Node *Node::duplicate_from_editor(HashMap<const Node *, Node *> &r_duplimap, const HashMap<Ref<Resource>, Ref<Resource>> &p_resource_remap) const { - Node *dupe = _duplicate(DUPLICATE_SIGNALS | DUPLICATE_GROUPS | DUPLICATE_SCRIPTS | DUPLICATE_USE_INSTANCING | DUPLICATE_FROM_EDITOR, &r_duplimap); + Node *dupe = _duplicate(DUPLICATE_SIGNALS | DUPLICATE_GROUPS | DUPLICATE_SCRIPTS | DUPLICATE_USE_INSTANTIATION | 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()) { @@ -2401,12 +2438,12 @@ void Node::replace_by(Node *p_node, bool p_keep_groups) { } Node *parent = data.parent; - int pos_in_parent = data.pos; + int index_in_parent = data.index; if (data.parent) { parent->remove_child(this); parent->add_child(p_node); - parent->move_child(p_node, pos_in_parent); + parent->move_child(p_node, index_in_parent); } while (get_child_count()) { @@ -2571,21 +2608,21 @@ static void _Node_debug_sn(Object *p_obj) { } #endif // DEBUG_ENABLED -void Node::_print_orphan_nodes() { - print_orphan_nodes(); -} - void Node::print_orphan_nodes() { #ifdef DEBUG_ENABLED ObjectDB::debug_objects(_Node_debug_sn); #endif } -void Node::queue_delete() { +void Node::queue_free() { + // There are users which instantiate multiple scene trees for their games. + // Use the node's own tree to handle its deletion when relevant. if (is_inside_tree()) { get_tree()->queue_delete(this); } else { - SceneTree::get_singleton()->queue_delete(this); + SceneTree *tree = SceneTree::get_singleton(); + ERR_FAIL_NULL_MSG(tree, "Can't queue free a node when no SceneTree is available."); + tree->queue_delete(this); } } @@ -2640,29 +2677,27 @@ void Node::clear_internal_tree_resource_paths() { } } -TypedArray<String> Node::get_configuration_warnings() const { - TypedArray<String> ret; +PackedStringArray Node::get_configuration_warnings() const { + PackedStringArray ret; Vector<String> warnings; if (GDVIRTUAL_CALL(_get_configuration_warnings, warnings)) { - for (int i = 0; i < warnings.size(); i++) { - ret.push_back(warnings[i]); - } + ret.append_array(warnings); } return ret; } String Node::get_configuration_warnings_as_string() const { - TypedArray<String> warnings = get_configuration_warnings(); - String all_warnings = String(); + PackedStringArray warnings = get_configuration_warnings(); + String all_warnings; for (int i = 0; i < warnings.size(); i++) { if (i > 0) { all_warnings += "\n\n"; } // Format as a bullet point list to make multiple warnings easier to distinguish // from each other. - all_warnings += String::utf8("• ") + String(warnings[i]); + all_warnings += String::utf8("• ") + warnings[i]; } return all_warnings; } @@ -2744,6 +2779,7 @@ void Node::_bind_methods() { 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_static_method("Node", D_METHOD("print_orphan_nodes"), &Node::print_orphan_nodes); ClassDB::bind_method(D_METHOD("add_sibling", "sibling", "force_readable_name"), &Node::add_sibling, DEFVAL(false)); ClassDB::bind_method(D_METHOD("set_name", "name"), &Node::set_name); @@ -2767,11 +2803,11 @@ void Node::_bind_methods() { ClassDB::bind_method(D_METHOD("is_ancestor_of", "node"), &Node::is_ancestor_of); ClassDB::bind_method(D_METHOD("is_greater_than", "node"), &Node::is_greater_than); ClassDB::bind_method(D_METHOD("get_path"), &Node::get_path); - ClassDB::bind_method(D_METHOD("get_path_to", "node"), &Node::get_path_to); + ClassDB::bind_method(D_METHOD("get_path_to", "node", "use_unique_path"), &Node::get_path_to, DEFVAL(false)); ClassDB::bind_method(D_METHOD("add_to_group", "group", "persistent"), &Node::add_to_group, DEFVAL(false)); ClassDB::bind_method(D_METHOD("remove_from_group", "group"), &Node::remove_from_group); ClassDB::bind_method(D_METHOD("is_in_group", "group"), &Node::is_in_group); - ClassDB::bind_method(D_METHOD("move_child", "child_node", "to_position"), &Node::move_child); + ClassDB::bind_method(D_METHOD("move_child", "child_node", "to_index"), &Node::move_child); ClassDB::bind_method(D_METHOD("get_groups"), &Node::_get_groups); ClassDB::bind_method(D_METHOD("set_owner", "owner"), &Node::set_owner); ClassDB::bind_method(D_METHOD("get_owner"), &Node::get_owner); @@ -2801,7 +2837,6 @@ void Node::_bind_methods() { 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_orphan_nodes"), &Node::_print_orphan_nodes); ClassDB::bind_method(D_METHOD("set_display_folded", "fold"), &Node::set_display_folded); ClassDB::bind_method(D_METHOD("is_displayed_folded"), &Node::is_displayed_folded); @@ -2815,7 +2850,7 @@ void Node::_bind_methods() { ClassDB::bind_method(D_METHOD("get_tree"), &Node::get_tree); ClassDB::bind_method(D_METHOD("create_tween"), &Node::create_tween); - ClassDB::bind_method(D_METHOD("duplicate", "flags"), &Node::duplicate, DEFVAL(DUPLICATE_USE_INSTANCING | DUPLICATE_SIGNALS | DUPLICATE_GROUPS | DUPLICATE_SCRIPTS)); + ClassDB::bind_method(D_METHOD("duplicate", "flags"), &Node::duplicate, DEFVAL(DUPLICATE_USE_INSTANTIATION | DUPLICATE_SIGNALS | DUPLICATE_GROUPS | DUPLICATE_SCRIPTS)); ClassDB::bind_method(D_METHOD("replace_by", "node", "keep_groups"), &Node::replace_by, DEFVAL(false)); ClassDB::bind_method(D_METHOD("set_scene_instance_load_placeholder", "load_placeholder"), &Node::set_scene_instance_load_placeholder); @@ -2825,7 +2860,7 @@ void Node::_bind_methods() { ClassDB::bind_method(D_METHOD("get_viewport"), &Node::get_viewport); - ClassDB::bind_method(D_METHOD("queue_free"), &Node::queue_delete); + ClassDB::bind_method(D_METHOD("queue_free"), &Node::queue_free); ClassDB::bind_method(D_METHOD("request_ready"), &Node::request_ready); @@ -2921,7 +2956,7 @@ void Node::_bind_methods() { BIND_ENUM_CONSTANT(DUPLICATE_SIGNALS); BIND_ENUM_CONSTANT(DUPLICATE_GROUPS); BIND_ENUM_CONSTANT(DUPLICATE_SCRIPTS); - BIND_ENUM_CONSTANT(DUPLICATE_USE_INSTANCING); + BIND_ENUM_CONSTANT(DUPLICATE_USE_INSTANTIATION); BIND_ENUM_CONSTANT(INTERNAL_MODE_DISABLED); BIND_ENUM_CONSTANT(INTERNAL_MODE_FRONT); @@ -2961,7 +2996,7 @@ void Node::_bind_methods() { } String Node::_get_name_num_separator() { - switch (ProjectSettings::get_singleton()->get("editor/node_naming/name_num_separator").operator int()) { + switch (GLOBAL_GET("editor/node_naming/name_num_separator").operator int()) { case 0: return ""; case 1: diff --git a/scene/main/node.h b/scene/main/node.h index 13a938ef97..574f5063e8 100644 --- a/scene/main/node.h +++ b/scene/main/node.h @@ -57,7 +57,7 @@ public: DUPLICATE_SIGNALS = 1, DUPLICATE_GROUPS = 2, DUPLICATE_SCRIPTS = 4, - DUPLICATE_USE_INSTANCING = 8, + DUPLICATE_USE_INSTANTIATION = 8, #ifdef TOOLS_ENABLED DUPLICATE_FROM_EDITOR = 16, #endif @@ -91,6 +91,7 @@ private: SceneTree::Group *group = nullptr; }; + // This Data struct is to avoid namespace pollution in derived classes. struct Data { String scene_file_path; Ref<SceneState> instance_state; @@ -104,7 +105,7 @@ private: int internal_children_front = 0; int internal_children_back = 0; - int pos = -1; + int index = -1; int depth = -1; int blocked = 0; // Safeguard that throws an error when attempting to modify the tree in a harmful way while being traversed. StringName name; @@ -172,7 +173,6 @@ private: void _propagate_ready(); void _propagate_exit_tree(); void _propagate_after_exit_tree(); - void _print_orphan_nodes(); void _propagate_process_owner(Node *p_owner, int p_pause_notification, int p_enabled_notification); void _propagate_groups_dirty(); Array _get_node_and_resource(const NodePath &p_path); @@ -186,8 +186,8 @@ private: Error _rpc_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error); Error _rpc_id_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error); - _FORCE_INLINE_ bool _is_internal_front() const { return data.parent && data.pos < data.parent->data.internal_children_front; } - _FORCE_INLINE_ bool _is_internal_back() const { return data.parent && data.pos >= data.parent->data.children.size() - data.parent->data.internal_children_back; } + _FORCE_INLINE_ bool _is_internal_front() const { return data.parent && data.index < data.parent->data.internal_children_front; } + _FORCE_INLINE_ bool _is_internal_back() const { return data.parent && data.index >= data.parent->data.children.size() - data.parent->data.internal_children_back; } friend class SceneTree; @@ -331,7 +331,7 @@ public: bool is_greater_than(const Node *p_node) const; NodePath get_path() const; - NodePath get_path_to(const Node *p_node) const; + NodePath get_path_to(const Node *p_node, bool p_use_unique_path = false) const; Node *find_common_parent_with(const Node *p_node) const; void add_to_group(const StringName &p_identifier, bool p_persistent = false); @@ -346,8 +346,8 @@ public: void get_groups(List<GroupInfo> *p_groups) const; int get_persistent_group_count() const; - void move_child(Node *p_child, int p_pos); - void _move_child(Node *p_child, int p_pos, bool p_ignore_end = false); + void move_child(Node *p_child, int p_index); + void _move_child(Node *p_child, int p_index, bool p_ignore_end = false); void set_owner(Node *p_owner); Node *get_owner() const; @@ -459,7 +459,7 @@ public: #endif static String adjust_name_casing(const String &p_name); - void queue_delete(); + void queue_free(); //hacks for speed static void init_node_hrcr(); @@ -477,7 +477,7 @@ public: _FORCE_INLINE_ Viewport *get_viewport() const { return data.viewport; } - virtual TypedArray<String> get_configuration_warnings() const; + virtual PackedStringArray get_configuration_warnings() const; String get_configuration_warnings_as_string() const; void update_configuration_warnings(); diff --git a/scene/main/scene_tree.cpp b/scene/main/scene_tree.cpp index c18aa5aaa2..ceb5b76ff2 100644 --- a/scene/main/scene_tree.cpp +++ b/scene/main/scene_tree.cpp @@ -44,6 +44,7 @@ #include "node.h" #include "scene/animation/tween.h" #include "scene/debugger/scene_debugger.h" +#include "scene/gui/control.h" #include "scene/main/multiplayer_api.h" #include "scene/main/viewport.h" #include "scene/resources/environment.h" @@ -105,10 +106,10 @@ bool SceneTreeTimer::is_ignore_time_scale() { } void SceneTreeTimer::release_connections() { - List<Connection> connections; - get_all_signal_connections(&connections); + List<Connection> signal_connections; + get_all_signal_connections(&signal_connections); - for (const Connection &connection : connections) { + for (const Connection &connection : signal_connections) { disconnect(connection.signal.get_name(), connection.callable); } } @@ -207,15 +208,15 @@ void SceneTree::_update_group_order(Group &g, bool p_use_priority) { return; } - Node **nodes = g.nodes.ptrw(); - int node_count = g.nodes.size(); + Node **gr_nodes = g.nodes.ptrw(); + int gr_node_count = g.nodes.size(); if (p_use_priority) { SortArray<Node *, Node::ComparatorWithPriority> node_sort; - node_sort.sort(nodes, node_count); + node_sort.sort(gr_nodes, gr_node_count); } else { SortArray<Node *, Node::Comparator> node_sort; - node_sort.sort(nodes, node_count); + node_sort.sort(gr_nodes, gr_node_count); } g.changed = false; } @@ -253,36 +254,36 @@ void SceneTree::call_group_flagsp(uint32_t p_call_flags, const StringName &p_gro _update_group_order(g); Vector<Node *> nodes_copy = g.nodes; - Node **nodes = nodes_copy.ptrw(); - int node_count = nodes_copy.size(); + Node **gr_nodes = nodes_copy.ptrw(); + int gr_node_count = nodes_copy.size(); call_lock++; if (p_call_flags & GROUP_CALL_REVERSE) { - for (int i = node_count - 1; i >= 0; i--) { - if (call_lock && call_skip.has(nodes[i])) { + for (int i = gr_node_count - 1; i >= 0; i--) { + if (call_lock && call_skip.has(gr_nodes[i])) { continue; } if (!(p_call_flags & GROUP_CALL_DEFERRED)) { Callable::CallError ce; - nodes[i]->callp(p_function, p_args, p_argcount, ce); + gr_nodes[i]->callp(p_function, p_args, p_argcount, ce); } else { - MessageQueue::get_singleton()->push_callp(nodes[i], p_function, p_args, p_argcount); + MessageQueue::get_singleton()->push_callp(gr_nodes[i], p_function, p_args, p_argcount); } } } else { - for (int i = 0; i < node_count; i++) { - if (call_lock && call_skip.has(nodes[i])) { + for (int i = 0; i < gr_node_count; i++) { + if (call_lock && call_skip.has(gr_nodes[i])) { continue; } if (!(p_call_flags & GROUP_CALL_DEFERRED)) { Callable::CallError ce; - nodes[i]->callp(p_function, p_args, p_argcount, ce); + gr_nodes[i]->callp(p_function, p_args, p_argcount, ce); } else { - MessageQueue::get_singleton()->push_callp(nodes[i], p_function, p_args, p_argcount); + MessageQueue::get_singleton()->push_callp(gr_nodes[i], p_function, p_args, p_argcount); } } } @@ -306,34 +307,34 @@ void SceneTree::notify_group_flags(uint32_t p_call_flags, const StringName &p_gr _update_group_order(g); Vector<Node *> nodes_copy = g.nodes; - Node **nodes = nodes_copy.ptrw(); - int node_count = nodes_copy.size(); + Node **gr_nodes = nodes_copy.ptrw(); + int gr_node_count = nodes_copy.size(); call_lock++; if (p_call_flags & GROUP_CALL_REVERSE) { - for (int i = node_count - 1; i >= 0; i--) { - if (call_lock && call_skip.has(nodes[i])) { + for (int i = gr_node_count - 1; i >= 0; i--) { + if (call_lock && call_skip.has(gr_nodes[i])) { continue; } if (!(p_call_flags & GROUP_CALL_DEFERRED)) { - nodes[i]->notification(p_notification); + gr_nodes[i]->notification(p_notification); } else { - MessageQueue::get_singleton()->push_notification(nodes[i], p_notification); + MessageQueue::get_singleton()->push_notification(gr_nodes[i], p_notification); } } } else { - for (int i = 0; i < node_count; i++) { - if (call_lock && call_skip.has(nodes[i])) { + for (int i = 0; i < gr_node_count; i++) { + if (call_lock && call_skip.has(gr_nodes[i])) { continue; } if (!(p_call_flags & GROUP_CALL_DEFERRED)) { - nodes[i]->notification(p_notification); + gr_nodes[i]->notification(p_notification); } else { - MessageQueue::get_singleton()->push_notification(nodes[i], p_notification); + MessageQueue::get_singleton()->push_notification(gr_nodes[i], p_notification); } } } @@ -357,34 +358,34 @@ void SceneTree::set_group_flags(uint32_t p_call_flags, const StringName &p_group _update_group_order(g); Vector<Node *> nodes_copy = g.nodes; - Node **nodes = nodes_copy.ptrw(); - int node_count = nodes_copy.size(); + Node **gr_nodes = nodes_copy.ptrw(); + int gr_node_count = nodes_copy.size(); call_lock++; if (p_call_flags & GROUP_CALL_REVERSE) { - for (int i = node_count - 1; i >= 0; i--) { - if (call_lock && call_skip.has(nodes[i])) { + for (int i = gr_node_count - 1; i >= 0; i--) { + if (call_lock && call_skip.has(gr_nodes[i])) { continue; } if (!(p_call_flags & GROUP_CALL_DEFERRED)) { - nodes[i]->set(p_name, p_value); + gr_nodes[i]->set(p_name, p_value); } else { - MessageQueue::get_singleton()->push_set(nodes[i], p_name, p_value); + MessageQueue::get_singleton()->push_set(gr_nodes[i], p_name, p_value); } } } else { - for (int i = 0; i < node_count; i++) { - if (call_lock && call_skip.has(nodes[i])) { + for (int i = 0; i < gr_node_count; i++) { + if (call_lock && call_skip.has(gr_nodes[i])) { continue; } if (!(p_call_flags & GROUP_CALL_DEFERRED)) { - nodes[i]->set(p_name, p_value); + gr_nodes[i]->set(p_name, p_value); } else { - MessageQueue::get_singleton()->push_set(nodes[i], p_name, p_value); + MessageQueue::get_singleton()->push_set(gr_nodes[i], p_name, p_value); } } } @@ -484,7 +485,7 @@ bool SceneTree::process(double p_time) { #ifndef _3D_DISABLED if (Engine::get_singleton()->is_editor_hint()) { //simple hack to reload fallback environment if it changed from editor - String env_path = ProjectSettings::get_singleton()->get(SNAME("rendering/environment/defaults/default_environment")); + String env_path = GLOBAL_GET(SNAME("rendering/environment/defaults/default_environment")); env_path = env_path.strip_edges(); //user may have added a space or two String cpath; Ref<Environment> fallback = get_root()->get_world_3d()->get_fallback_environment(); @@ -510,7 +511,7 @@ bool SceneTree::process(double p_time) { return _quit; } -void SceneTree::process_timers(float p_delta, bool p_physics_frame) { +void SceneTree::process_timers(double p_delta, bool p_physics_frame) { List<Ref<SceneTreeTimer>>::Element *L = timers.back(); //last element for (List<Ref<SceneTreeTimer>>::Element *E = timers.front(); E;) { @@ -542,7 +543,7 @@ void SceneTree::process_timers(float p_delta, bool p_physics_frame) { } } -void SceneTree::process_tweens(float p_delta, bool p_physics) { +void SceneTree::process_tweens(double p_delta, bool p_physics) { // This methods works similarly to how SceneTreeTimers are handled. List<Ref<Tween>>::Element *L = tweens.back(); @@ -846,13 +847,13 @@ void SceneTree::_notify_group_pause(const StringName &p_group, int p_notificatio //performance is not lost because only if something is added/removed the vector is copied. Vector<Node *> nodes_copy = g.nodes; - int node_count = nodes_copy.size(); - Node **nodes = nodes_copy.ptrw(); + int gr_node_count = nodes_copy.size(); + Node **gr_nodes = nodes_copy.ptrw(); call_lock++; - for (int i = 0; i < node_count; i++) { - Node *n = nodes[i]; + for (int i = 0; i < gr_node_count; i++) { + Node *n = gr_nodes[i]; if (call_lock && call_skip.has(n)) { continue; } @@ -865,7 +866,7 @@ void SceneTree::_notify_group_pause(const StringName &p_group, int p_notificatio } n->notification(p_notification); - //ERR_FAIL_COND(node_count != g.nodes.size()); + //ERR_FAIL_COND(gr_node_count != g.nodes.size()); } call_lock--; @@ -890,17 +891,19 @@ void SceneTree::_call_input_pause(const StringName &p_group, CallInputType p_cal //performance is not lost because only if something is added/removed the vector is copied. Vector<Node *> nodes_copy = g.nodes; - int node_count = nodes_copy.size(); - Node **nodes = nodes_copy.ptrw(); + int gr_node_count = nodes_copy.size(); + Node **gr_nodes = nodes_copy.ptrw(); call_lock++; - for (int i = node_count - 1; i >= 0; i--) { + Vector<ObjectID> no_context_node_ids; // Nodes may be deleted due to this shortcut input. + + for (int i = gr_node_count - 1; i >= 0; i--) { if (p_viewport->is_input_handled()) { break; } - Node *n = nodes[i]; + Node *n = gr_nodes[i]; if (call_lock && call_skip.has(n)) { continue; } @@ -913,9 +916,22 @@ void SceneTree::_call_input_pause(const StringName &p_group, CallInputType p_cal case CALL_INPUT_TYPE_INPUT: n->_call_input(p_input); break; - case CALL_INPUT_TYPE_SHORTCUT_INPUT: + case CALL_INPUT_TYPE_SHORTCUT_INPUT: { + const Control *c = Object::cast_to<Control>(n); + if (c) { + // If calling shortcut input on a control, ensure it respects the shortcut context. + // Shortcut context (based on focus) only makes sense for controls (UI), so don't need to worry about it for nodes + if (c->get_shortcut_context() == nullptr) { + no_context_node_ids.append(n->get_instance_id()); + continue; + } + if (!c->is_focus_owner_in_shortcut_context()) { + continue; + } + } n->_call_shortcut_input(p_input); break; + } case CALL_INPUT_TYPE_UNHANDLED_INPUT: n->_call_unhandled_input(p_input); break; @@ -925,6 +941,13 @@ void SceneTree::_call_input_pause(const StringName &p_group, CallInputType p_cal } } + for (const ObjectID &id : no_context_node_ids) { + Node *n = Object::cast_to<Node>(ObjectDB::get_instance(id)); + if (n) { + n->_call_shortcut_input(p_input); + } + } + call_lock--; if (call_lock == 0) { call_skip.clear(); @@ -1085,6 +1108,7 @@ void SceneTree::_change_scene(Node *p_to) { if (p_to) { current_scene = p_to; root->add_child(p_to); + root->update_mouse_cursor_shape(); } } @@ -1366,7 +1390,7 @@ SceneTree::SceneTree() { root = memnew(Window); root->set_process_mode(Node::PROCESS_MODE_PAUSABLE); root->set_name("root"); - root->set_title(ProjectSettings::get_singleton()->get("application/config/name")); + root->set_title(GLOBAL_GET("application/config/name")); #ifndef _3D_DISABLED if (!root->get_world_3d().is_valid()) { @@ -1389,6 +1413,9 @@ SceneTree::SceneTree() { ProjectSettings::get_singleton()->set_custom_property_info("rendering/anti_aliasing/quality/msaa_3d", PropertyInfo(Variant::INT, "rendering/anti_aliasing/quality/msaa_3d", PROPERTY_HINT_ENUM, String::utf8("Disabled (Fastest),2× (Average),4× (Slow),8× (Slowest)"))); root->set_msaa_3d(Viewport::MSAA(msaa_mode_3d)); + const bool transparent_background = GLOBAL_DEF("rendering/transparent_background", false); + root->set_transparent_background(transparent_background); + const int ssaa_mode = GLOBAL_DEF_BASIC("rendering/anti_aliasing/quality/screen_space_aa", 0); ProjectSettings::get_singleton()->set_custom_property_info("rendering/anti_aliasing/quality/screen_space_aa", PropertyInfo(Variant::INT, "rendering/anti_aliasing/quality/screen_space_aa", PROPERTY_HINT_ENUM, "Disabled (Fastest),FXAA (Fast)")); root->set_screen_space_aa(Viewport::ScreenSpaceAA(ssaa_mode)); @@ -1420,7 +1447,7 @@ SceneTree::SceneTree() { ProjectSettings::get_singleton()->set_custom_property_info("rendering/vrs/texture", PropertyInfo(Variant::STRING, "rendering/vrs/texture", - PROPERTY_HINT_FILE, "*.png")); + PROPERTY_HINT_FILE, "*.bmp,*.png,*.tga,*.webp")); if (vrs_mode == 1 && !vrs_texture_path.is_empty()) { Ref<Image> vrs_image; vrs_image.instantiate(); diff --git a/scene/main/scene_tree.h b/scene/main/scene_tree.h index 031a331a99..a460e40597 100644 --- a/scene/main/scene_tree.h +++ b/scene/main/scene_tree.h @@ -180,8 +180,8 @@ private: void node_added(Node *p_node); void node_removed(Node *p_node); void node_renamed(Node *p_node); - void process_timers(float p_delta, bool p_physics_frame); - void process_tweens(float p_delta, bool p_physics_frame); + void process_timers(double p_delta, bool p_physics_frame); + void process_tweens(double p_delta, bool p_physics_frame); Group *add_to_group(const StringName &p_group, Node *p_node); void remove_from_group(const StringName &p_group, Node *p_node); diff --git a/scene/main/shader_globals_override.cpp b/scene/main/shader_globals_override.cpp index 13034c5447..455b8c6866 100644 --- a/scene/main/shader_globals_override.cpp +++ b/scene/main/shader_globals_override.cpp @@ -271,8 +271,8 @@ void ShaderGlobalsOverride::_notification(int p_what) { } } -TypedArray<String> ShaderGlobalsOverride::get_configuration_warnings() const { - TypedArray<String> warnings = Node::get_configuration_warnings(); +PackedStringArray ShaderGlobalsOverride::get_configuration_warnings() const { + PackedStringArray warnings = Node::get_configuration_warnings(); if (!active) { warnings.push_back(RTR("ShaderGlobalsOverride is not active because another node of the same type is in the scene.")); diff --git a/scene/main/shader_globals_override.h b/scene/main/shader_globals_override.h index af99bf9aa7..f3d0074f28 100644 --- a/scene/main/shader_globals_override.h +++ b/scene/main/shader_globals_override.h @@ -58,7 +58,7 @@ protected: static void _bind_methods(); public: - TypedArray<String> get_configuration_warnings() const override; + PackedStringArray get_configuration_warnings() const override; ShaderGlobalsOverride(); }; diff --git a/scene/main/timer.cpp b/scene/main/timer.cpp index bb9359ef59..210b60171a 100644 --- a/scene/main/timer.cpp +++ b/scene/main/timer.cpp @@ -180,8 +180,8 @@ void Timer::_set_process(bool p_process, bool p_force) { processing = p_process; } -TypedArray<String> Timer::get_configuration_warnings() const { - TypedArray<String> warnings = Node::get_configuration_warnings(); +PackedStringArray Timer::get_configuration_warnings() const { + PackedStringArray warnings = Node::get_configuration_warnings(); if (wait_time < 0.05 - CMP_EPSILON) { warnings.push_back(RTR("Very low timer wait times (< 0.05 seconds) may behave in significantly different ways depending on the rendered or physics frame rate.\nConsider using a script's process loop instead of relying on a Timer for very low wait times.")); diff --git a/scene/main/timer.h b/scene/main/timer.h index 8785d31a8a..53503e31b2 100644 --- a/scene/main/timer.h +++ b/scene/main/timer.h @@ -73,7 +73,7 @@ public: double get_time_left() const; - TypedArray<String> get_configuration_warnings() const override; + PackedStringArray get_configuration_warnings() const override; void set_timer_process_callback(TimerProcessCallback p_callback); TimerProcessCallback get_timer_process_callback() const; diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp index 5295de5c09..7430a0a835 100644 --- a/scene/main/viewport.cpp +++ b/scene/main/viewport.cpp @@ -60,8 +60,8 @@ #include "servers/rendering/rendering_server_globals.h" void ViewportTexture::setup_local_to_scene() { - Node *local_scene = get_local_scene(); - if (!local_scene) { + Node *loc_scene = get_local_scene(); + if (!loc_scene) { return; } @@ -71,7 +71,7 @@ void ViewportTexture::setup_local_to_scene() { vp = nullptr; - Node *vpn = local_scene->get_node(path); + Node *vpn = loc_scene->get_node(path); ERR_FAIL_COND_MSG(!vpn, "ViewportTexture: Path to node is invalid."); vp = Object::cast_to<Viewport>(vpn); @@ -362,6 +362,8 @@ void Viewport::_notification(int p_what) { current_canvas = find_world_2d()->get_canvas(); RenderingServer::get_singleton()->viewport_attach_canvas(viewport, current_canvas); + RenderingServer::get_singleton()->viewport_set_canvas_transform(viewport, current_canvas, canvas_transform); + RenderingServer::get_singleton()->viewport_set_canvas_cull_mask(viewport, canvas_cull_mask); _update_audio_listener_2d(); #ifndef _3D_DISABLED RenderingServer::get_singleton()->viewport_set_scenario(viewport, find_world_3d()->get_scenario()); @@ -490,6 +492,7 @@ void Viewport::_notification(int p_what) { } break; case NOTIFICATION_WM_WINDOW_FOCUS_OUT: { + _gui_cancel_tooltip(); _drop_physics_mouseover(); if (gui.mouse_focus && !gui.forced_mouse_focus) { _drop_mouse_focus(); @@ -499,6 +502,13 @@ void Viewport::_notification(int p_what) { // exit event if the change in focus results in the mouse exiting // the window. } break; + + case NOTIFICATION_PREDELETE: { + if (gui_parent) { + gui_parent->gui.tooltip_popup = nullptr; + gui_parent->gui.tooltip_label = nullptr; + } + } break; } } @@ -628,19 +638,19 @@ void Viewport::_process_picking() { PhysicsDirectSpaceState2D::ShapeResult res[64]; for (const CanvasLayer *E : canvas_layers) { - Transform2D canvas_transform; + Transform2D canvas_layer_transform; ObjectID canvas_layer_id; if (E) { // A descendant CanvasLayer. - canvas_transform = E->get_transform(); + canvas_layer_transform = E->get_transform(); canvas_layer_id = E->get_instance_id(); } else { // This Viewport's builtin canvas. - canvas_transform = get_canvas_transform(); + canvas_layer_transform = get_canvas_transform(); canvas_layer_id = ObjectID(); } - Vector2 point = canvas_transform.affine_inverse().xform(pos); + Vector2 point = canvas_layer_transform.affine_inverse().xform(pos); PhysicsDirectSpaceState2D::PointParameters point_params; point_params.position = point; @@ -795,11 +805,20 @@ void Viewport::_set_size(const Size2i &p_size, const Size2i &p_size_2d_override, stretch_transform = p_stretch_transform; to_screen_rect = p_to_screen_rect; - if (p_allocated) { - RS::get_singleton()->viewport_set_size(viewport, size.width, size.height); - } else { - RS::get_singleton()->viewport_set_size(viewport, 0, 0); - } +#ifndef _3D_DISABLED + if (!use_xr) { +#endif + + if (p_allocated) { + RS::get_singleton()->viewport_set_size(viewport, size.width, size.height); + } else { + RS::get_singleton()->viewport_set_size(viewport, 0, 0); + } + +#ifndef _3D_DISABLED + } // if (!use_xr) +#endif + _update_global_transform(); update_configuration_warnings(); @@ -813,6 +832,19 @@ void Viewport::_set_size(const Size2i &p_size, const Size2i &p_size_2d_override, } Size2i Viewport::_get_size() const { +#ifndef _3D_DISABLED + if (use_xr) { + if (XRServer::get_singleton() != nullptr) { + Ref<XRInterface> xr_interface = XRServer::get_singleton()->get_primary_interface(); + if (xr_interface.is_valid() && xr_interface->is_initialized()) { + Size2 xr_size = xr_interface->get_render_target_size(); + return (Size2i)xr_size; + } + } + return Size2i(); + } +#endif // _3D_DISABLED + return size; } @@ -964,11 +996,6 @@ void Viewport::set_world_2d(const Ref<World2D> &p_world_2d) { return; } - if (parent && parent->find_world_2d() == p_world_2d) { - WARN_PRINT("Unable to use parent world_2d as world_2d"); - return; - } - if (is_inside_tree()) { RenderingServer::get_singleton()->viewport_remove_canvas(viewport, current_canvas); } @@ -1132,9 +1159,7 @@ void Viewport::_gui_cancel_tooltip() { gui.tooltip_timer = Ref<SceneTreeTimer>(); } if (gui.tooltip_popup) { - gui.tooltip_popup->queue_delete(); - gui.tooltip_popup = nullptr; - gui.tooltip_label = nullptr; + gui.tooltip_popup->queue_free(); } } @@ -1197,8 +1222,6 @@ void Viewport::_gui_show_tooltip() { // Remove previous popup if we change something. if (gui.tooltip_popup) { memdelete(gui.tooltip_popup); - gui.tooltip_popup = nullptr; - gui.tooltip_label = nullptr; } if (!tooltip_owner) { @@ -1230,16 +1253,23 @@ void Viewport::_gui_show_tooltip() { panel->set_flag(Window::FLAG_POPUP, false); panel->set_wrap_controls(true); panel->add_child(base_tooltip); + panel->gui_parent = this; gui.tooltip_popup = panel; tooltip_owner->add_child(gui.tooltip_popup); - Point2 tooltip_offset = ProjectSettings::get_singleton()->get("display/mouse_cursor/tooltip_position_offset"); + Point2 tooltip_offset = GLOBAL_GET("display/mouse_cursor/tooltip_position_offset"); Rect2 r(gui.tooltip_pos + tooltip_offset, gui.tooltip_popup->get_contents_minimum_size()); + r.size = r.size.min(panel->get_max_size()); Window *window = gui.tooltip_popup->get_parent_visible_window(); - Rect2i vr = window->get_usable_parent_rect(); + Rect2i vr; + if (gui.tooltip_popup->is_embedded()) { + vr = gui.tooltip_popup->_get_embedder()->get_visible_rect(); + } else { + vr = window->get_usable_parent_rect(); + } if (r.size.x + r.position.x > vr.size.x + vr.position.x) { // Place it in the opposite direction. If it fails, just hug the border. @@ -1377,10 +1407,6 @@ Control *Viewport::gui_find_control(const Point2 &p_global) { } Control *Viewport::_gui_find_control_at_pos(CanvasItem *p_node, const Point2 &p_global, const Transform2D &p_xform, Transform2D &r_inv_xform) { - if (Object::cast_to<Viewport>(p_node)) { - return nullptr; - } - if (!p_node->is_visible()) { return nullptr; // Canvas item hidden, discard. } @@ -1464,7 +1490,6 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) { gui.key_event_accepted = false; Point2 mpos = mb->get_position(); - gui.last_mouse_pos = mpos; if (mb->is_pressed()) { Size2 pos = mpos; if (gui.mouse_focus_mask != MouseButton::NONE) { @@ -1538,44 +1563,15 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) { set_input_as_handled(); } - if (gui.drag_data.get_type() != Variant::NIL && mb->get_button_index() == MouseButton::LEFT) { + if (gui.dragging && mb->get_button_index() == MouseButton::LEFT) { // Alternate drop use (when using force_drag(), as proposed by #5342). - gui.drag_successful = false; - if (gui.mouse_focus) { - gui.drag_successful = _gui_drop(gui.mouse_focus, pos, false); - } - - gui.drag_data = Variant(); - gui.dragging = false; - - Control *drag_preview = _gui_get_drag_preview(); - if (drag_preview) { - memdelete(drag_preview); - gui.drag_preview_id = ObjectID(); - } - _propagate_viewport_notification(this, NOTIFICATION_DRAG_END); - // Change mouse accordingly. + _perform_drop(gui.mouse_focus, pos); } _gui_cancel_tooltip(); } else { - if (gui.drag_data.get_type() != Variant::NIL && mb->get_button_index() == MouseButton::LEFT) { - gui.drag_successful = false; - if (gui.drag_mouse_over) { - gui.drag_successful = _gui_drop(gui.drag_mouse_over, gui.drag_mouse_over_pos, false); - } - - Control *drag_preview = _gui_get_drag_preview(); - if (drag_preview) { - memdelete(drag_preview); - gui.drag_preview_id = ObjectID(); - } - - gui.drag_data = Variant(); - gui.dragging = false; - gui.drag_mouse_over = nullptr; - _propagate_viewport_notification(this, NOTIFICATION_DRAG_END); - // Change mouse accordingly. + if (gui.dragging && mb->get_button_index() == MouseButton::LEFT) { + _perform_drop(gui.drag_mouse_over, gui.drag_mouse_over_pos); } gui.mouse_focus_mask &= ~mouse_button_to_mask(mb->get_button_index()); // Remove from mask. @@ -1618,8 +1614,6 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) { gui.key_event_accepted = false; Point2 mpos = mm->get_position(); - gui.last_mouse_pos = mpos; - // Drag & drop. if (!gui.drag_attempted && gui.mouse_focus && (mm->get_button_mask() & MouseButton::MASK_LEFT) != MouseButton::NONE) { gui.drag_accum += mm->get_relative(); @@ -1631,7 +1625,7 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) { Control *control = Object::cast_to<Control>(ci); if (control) { gui.dragging = true; - gui.drag_data = control->get_drag_data(control->get_global_transform_with_canvas().affine_inverse().xform(mpos) - gui.drag_accum); + gui.drag_data = control->get_drag_data(control->get_global_transform_with_canvas().affine_inverse().xform(mpos - gui.drag_accum)); if (gui.drag_data.get_type() != Variant::NIL) { gui.mouse_focus = nullptr; gui.forced_mouse_focus = false; @@ -1661,7 +1655,7 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) { } gui.drag_attempted = true; - if (gui.drag_data.get_type() != Variant::NIL) { + if (gui.dragging) { _propagate_viewport_notification(this, NOTIFICATION_DRAG_BEGIN); } } @@ -1677,6 +1671,9 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) { _gui_cancel_tooltip(); if (over) { + if (!gui.mouse_over) { + _drop_physics_mouseover(); + } _gui_call_notification(over, Control::NOTIFICATION_MOUSE_ENTER); gui.mouse_over = over; } @@ -1774,7 +1771,7 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) { } } - if (gui.drag_data.get_type() != Variant::NIL) { + if (gui.dragging) { // Handle drag & drop. Control *drag_preview = _gui_get_drag_preview(); @@ -1976,6 +1973,12 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) { } if (mm.is_null() && mb.is_null() && p_event->is_action_type()) { + if (gui.dragging && p_event->is_action_pressed("ui_cancel") && Input::get_singleton()->is_action_just_pressed("ui_cancel")) { + _perform_drop(); + set_input_as_handled(); + return; + } + if (gui.key_focus && !gui.key_focus->is_visible_in_tree()) { gui.key_focus->release_focus(); } @@ -2057,6 +2060,27 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) { } } +void Viewport::_perform_drop(Control *p_control, Point2 p_pos) { + // Without any arguments, simply cancel Drag and Drop. + if (p_control) { + gui.drag_successful = _gui_drop(p_control, p_pos, false); + } else { + gui.drag_successful = false; + } + + Control *drag_preview = _gui_get_drag_preview(); + if (drag_preview) { + memdelete(drag_preview); + gui.drag_preview_id = ObjectID(); + } + + gui.drag_data = Variant(); + gui.dragging = false; + gui.drag_mouse_over = nullptr; + _propagate_viewport_notification(this, NOTIFICATION_DRAG_END); + get_base_window()->update_mouse_cursor_shape(); +} + void Viewport::_gui_cleanup_internal_state(Ref<InputEvent> p_event) { ERR_FAIL_COND(p_event.is_null()); @@ -2073,7 +2097,7 @@ List<Control *>::Element *Viewport::_gui_add_root_control(Control *p_control) { return gui.roots.push_back(p_control); } -void Viewport::_gui_set_root_order_dirty() { +void Viewport::gui_set_root_order_dirty() { gui.roots_order_dirty = true; } @@ -2641,6 +2665,11 @@ bool Viewport::_sub_windows_forward_input(const Ref<InputEvent> &p_event) { } else { gui.subwindow_resize_mode = _sub_window_get_resize_margin(sw.window, mb->get_position()); if (gui.subwindow_resize_mode != SUB_WINDOW_RESIZE_DISABLED) { + if (gui.subwindow_focused != sw.window) { + // Refocus. + _sub_window_grab_focus(sw.window); + } + gui.subwindow_resize_from_rect = r; gui.subwindow_drag_from = mb->get_position(); gui.subwindow_drag = SUB_WINDOW_DRAG_RESIZE; @@ -2733,6 +2762,11 @@ void Viewport::push_input(const Ref<InputEvent> &p_event, bool p_local_coords) { ev = p_event; } + Ref<InputEventMouse> me = ev; + if (me.is_valid()) { + gui.last_mouse_pos = me->get_position(); + } + if (is_embedding_subwindows() && _sub_windows_forward_input(ev)) { set_input_as_handled(); return; @@ -2777,7 +2811,7 @@ void Viewport::push_unhandled_input(const Ref<InputEvent> &p_event, bool p_local } // Shortcut Input. - if (Object::cast_to<InputEventKey>(*ev) != nullptr || Object::cast_to<InputEventShortcut>(*ev) != nullptr) { + if (Object::cast_to<InputEventKey>(*ev) != nullptr || Object::cast_to<InputEventShortcut>(*ev) != nullptr || Object::cast_to<InputEventJoypadButton>(*ev) != nullptr) { get_tree()->_call_input_pause(shortcut_input_group, SceneTree::CALL_INPUT_TYPE_SHORTCUT_INPUT, ev, this); } @@ -2850,8 +2884,8 @@ Variant Viewport::gui_get_drag_data() const { return gui.drag_data; } -TypedArray<String> Viewport::get_configuration_warnings() const { - TypedArray<String> warnings = Node::get_configuration_warnings(); +PackedStringArray Viewport::get_configuration_warnings() const { + PackedStringArray warnings = Node::get_configuration_warnings(); if (size.x <= 1 || size.y <= 1) { warnings.push_back(RTR("The Viewport size must be greater than or equal to 2 pixels on both dimensions to render anything.")); @@ -3015,8 +3049,6 @@ bool Viewport::gui_is_drag_successful() const { } void Viewport::set_input_as_handled() { - _drop_physics_mouseover(); - if (!handle_input_locally) { ERR_FAIL_COND(!is_inside_tree()); Viewport *vp = this; @@ -3219,6 +3251,29 @@ Transform2D Viewport::get_screen_transform() const { return _get_input_pre_xform().affine_inverse() * get_final_transform(); } +void Viewport::set_canvas_cull_mask(uint32_t p_canvas_cull_mask) { + canvas_cull_mask = p_canvas_cull_mask; + RenderingServer::get_singleton()->viewport_set_canvas_cull_mask(viewport, canvas_cull_mask); +} + +uint32_t Viewport::get_canvas_cull_mask() const { + return canvas_cull_mask; +} + +void Viewport::set_canvas_cull_mask_bit(uint32_t p_layer, bool p_enable) { + ERR_FAIL_UNSIGNED_INDEX(p_layer, 32); + if (p_enable) { + set_canvas_cull_mask(canvas_cull_mask | (1 << p_layer)); + } else { + set_canvas_cull_mask(canvas_cull_mask & (~(1 << p_layer))); + } +} + +bool Viewport::get_canvas_cull_mask_bit(uint32_t p_layer) const { + ERR_FAIL_UNSIGNED_INDEX_V(p_layer, 32, false); + return (canvas_cull_mask & (1 << p_layer)); +} + #ifndef _3D_DISABLED AudioListener3D *Viewport::get_audio_listener_3d() const { return audio_listener_3d; @@ -3612,9 +3667,20 @@ void Viewport::_propagate_exit_world_3d(Node *p_node) { } void Viewport::set_use_xr(bool p_use_xr) { - use_xr = p_use_xr; + if (use_xr != p_use_xr) { + use_xr = p_use_xr; + + RS::get_singleton()->viewport_set_use_xr(viewport, use_xr); - RS::get_singleton()->viewport_set_use_xr(viewport, use_xr); + if (!use_xr) { + // Set viewport to previous size when exiting XR. + if (size_allocated) { + RS::get_singleton()->viewport_set_size(viewport, size.width, size.height); + } else { + RS::get_singleton()->viewport_set_size(viewport, 0, 0); + } + } + } } bool Viewport::is_using_xr() { @@ -3690,6 +3756,7 @@ void Viewport::_bind_methods() { ClassDB::bind_method(D_METHOD("set_global_canvas_transform", "xform"), &Viewport::set_global_canvas_transform); ClassDB::bind_method(D_METHOD("get_global_canvas_transform"), &Viewport::get_global_canvas_transform); ClassDB::bind_method(D_METHOD("get_final_transform"), &Viewport::get_final_transform); + ClassDB::bind_method(D_METHOD("get_screen_transform"), &Viewport::get_screen_transform); ClassDB::bind_method(D_METHOD("get_visible_rect"), &Viewport::get_visible_rect); ClassDB::bind_method(D_METHOD("set_transparent_background", "enable"), &Viewport::set_transparent_background); @@ -3778,6 +3845,12 @@ void Viewport::_bind_methods() { ClassDB::bind_method(D_METHOD("set_embedding_subwindows", "enable"), &Viewport::set_embedding_subwindows); ClassDB::bind_method(D_METHOD("is_embedding_subwindows"), &Viewport::is_embedding_subwindows); + ClassDB::bind_method(D_METHOD("set_canvas_cull_mask", "mask"), &Viewport::set_canvas_cull_mask); + ClassDB::bind_method(D_METHOD("get_canvas_cull_mask"), &Viewport::get_canvas_cull_mask); + + ClassDB::bind_method(D_METHOD("set_canvas_cull_mask_bit", "layer", "enable"), &Viewport::set_canvas_cull_mask_bit); + ClassDB::bind_method(D_METHOD("get_canvas_cull_mask_bit", "layer"), &Viewport::get_canvas_cull_mask_bit); + ClassDB::bind_method(D_METHOD("set_default_canvas_item_texture_repeat", "mode"), &Viewport::set_default_canvas_item_texture_repeat); ClassDB::bind_method(D_METHOD("get_default_canvas_item_texture_repeat"), &Viewport::get_default_canvas_item_texture_repeat); @@ -3883,6 +3956,7 @@ void Viewport::_bind_methods() { ADD_PROPERTYI(PropertyInfo(Variant::INT, "positional_shadow_atlas_quad_3", PROPERTY_HINT_ENUM, "Disabled,1 Shadow,4 Shadows,16 Shadows,64 Shadows,256 Shadows,1024 Shadows"), "set_positional_shadow_atlas_quadrant_subdiv", "get_positional_shadow_atlas_quadrant_subdiv", 3); ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM2D, "canvas_transform", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_canvas_transform", "get_canvas_transform"); ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM2D, "global_canvas_transform", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_global_canvas_transform", "get_global_canvas_transform"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "canvas_cull_mask", PROPERTY_HINT_LAYERS_2D_RENDER), "set_canvas_cull_mask", "get_canvas_cull_mask"); ADD_SIGNAL(MethodInfo("size_changed")); ADD_SIGNAL(MethodInfo("gui_focus_changed", PropertyInfo(Variant::OBJECT, "node", PROPERTY_HINT_RESOURCE_TYPE, "Control"))); @@ -4017,16 +4091,10 @@ Viewport::Viewport() { ProjectSettings::get_singleton()->set_custom_property_info("gui/timers/tooltip_delay_sec", PropertyInfo(Variant::FLOAT, "gui/timers/tooltip_delay_sec", PROPERTY_HINT_RANGE, "0,5,0.01,or_greater")); // No negative numbers #ifndef _3D_DISABLED - Viewport::Scaling3DMode scaling_3d_mode = (Viewport::Scaling3DMode)(int)GLOBAL_GET("rendering/scaling_3d/mode"); - set_scaling_3d_mode(scaling_3d_mode); - + set_scaling_3d_mode((Viewport::Scaling3DMode)(int)GLOBAL_GET("rendering/scaling_3d/mode")); set_scaling_3d_scale(GLOBAL_GET("rendering/scaling_3d/scale")); - - float fsr_sharpness = GLOBAL_GET("rendering/scaling_3d/fsr_sharpness"); - set_fsr_sharpness(fsr_sharpness); - - float texture_mipmap_bias = GLOBAL_GET("rendering/textures/default_filters/texture_mipmap_bias"); - set_texture_mipmap_bias(texture_mipmap_bias); + set_fsr_sharpness((float)GLOBAL_GET("rendering/scaling_3d/fsr_sharpness")); + set_texture_mipmap_bias((float)GLOBAL_GET("rendering/textures/default_filters/texture_mipmap_bias")); #endif // _3D_DISABLED set_sdf_oversize(sdf_oversize); // Set to server. @@ -4099,10 +4167,10 @@ DisplayServer::WindowID SubViewport::get_window_id() const { } Transform2D SubViewport::_stretch_transform() { - Transform2D transform = Transform2D(); + Transform2D transform; Size2i view_size_2d_override = _get_size_2d_override(); if (size_2d_override_stretch && view_size_2d_override.width > 0 && view_size_2d_override.height > 0) { - Size2 scale = _get_size() / view_size_2d_override; + Size2 scale = Size2(_get_size()) / Size2(view_size_2d_override); transform.scale(scale); } @@ -4110,7 +4178,7 @@ Transform2D SubViewport::_stretch_transform() { } Transform2D SubViewport::get_screen_transform() const { - Transform2D container_transform = Transform2D(); + Transform2D container_transform; SubViewportContainer *c = Object::cast_to<SubViewportContainer>(get_parent()); if (c) { if (c->is_stretch_enabled()) { @@ -4169,6 +4237,8 @@ void SubViewport::_bind_methods() { BIND_ENUM_CONSTANT(UPDATE_ALWAYS); } -SubViewport::SubViewport() {} +SubViewport::SubViewport() { + RS::get_singleton()->viewport_set_size(get_viewport_rid(), get_size().width, get_size().height); +} SubViewport::~SubViewport() {} diff --git a/scene/main/viewport.h b/scene/main/viewport.h index afea3ea56c..5659ee4000 100644 --- a/scene/main/viewport.h +++ b/scene/main/viewport.h @@ -208,6 +208,7 @@ private: friend class ViewportTexture; Viewport *parent = nullptr; + Viewport *gui_parent = nullptr; // Whose gui.tooltip_popup it is. AudioListener2D *audio_listener_2d = nullptr; Camera2D *camera_2d = nullptr; @@ -316,6 +317,8 @@ private: SDFOversize sdf_oversize = SDF_OVERSIZE_120_PERCENT; SDFScale sdf_scale = SDF_SCALE_50_PERCENT; + uint32_t canvas_cull_mask = 0xffffffff; // by default show everything + enum SubWindowDrag { SUB_WINDOW_DRAG_DISABLED, SUB_WINDOW_DRAG_MOVE, @@ -402,6 +405,7 @@ private: Control *_gui_find_control_at_pos(CanvasItem *p_node, const Point2 &p_global, const Transform2D &p_xform, Transform2D &r_inv_xform); void _gui_input_event(Ref<InputEvent> p_event); + void _perform_drop(Control *p_control = nullptr, Point2 p_pos = Point2()); void _gui_cleanup_internal_state(Ref<InputEvent> p_event); _FORCE_INLINE_ Transform2D _get_input_pre_xform() const; @@ -452,8 +456,6 @@ private: void _update_canvas_items(Node *p_node); - void _gui_set_root_order_dirty(); - friend class Window; void _sub_window_update_order(); @@ -510,6 +512,8 @@ public: Transform2D get_final_transform() const; + void gui_set_root_order_dirty(); + void set_transparent_background(bool p_enable); bool has_transparent_background() const; @@ -568,7 +572,7 @@ public: bool is_input_disabled() const; Vector2 get_mouse_position() const; - virtual void warp_mouse(const Vector2 &p_position); + void warp_mouse(const Vector2 &p_position); void set_physics_object_picking(bool p_enable); bool get_physics_object_picking(); @@ -581,7 +585,7 @@ public: void gui_release_focus(); Control *gui_get_focus_owner(); - TypedArray<String> get_configuration_warnings() const override; + PackedStringArray get_configuration_warnings() const override; void set_debug_draw(DebugDraw p_debug_draw); DebugDraw get_debug_draw() const; @@ -638,6 +642,12 @@ public: void pass_mouse_focus_to(Viewport *p_viewport, Control *p_control); + void set_canvas_cull_mask(uint32_t p_layers); + uint32_t get_canvas_cull_mask() const; + + void set_canvas_cull_mask_bit(uint32_t p_layer, bool p_enable); + bool get_canvas_cull_mask_bit(uint32_t p_layer) const; + virtual Transform2D get_screen_transform() const; #ifndef _3D_DISABLED diff --git a/scene/main/window.cpp b/scene/main/window.cpp index 04f56bb874..aff2c594d9 100644 --- a/scene/main/window.cpp +++ b/scene/main/window.cpp @@ -157,26 +157,18 @@ void Window::set_flag(Flags p_flag, bool p_enabled) { embedder->_sub_window_update(this); } else if (window_id != DisplayServer::INVALID_WINDOW_ID) { -#ifdef TOOLS_ENABLED - if ((p_flag != FLAG_POPUP) || !(Engine::get_singleton()->is_editor_hint() && get_tree()->get_edited_scene_root() && (get_tree()->get_edited_scene_root()->is_ancestor_of(this) || get_tree()->get_edited_scene_root() == this))) { + if (!is_in_edited_scene_root()) { DisplayServer::get_singleton()->window_set_flag(DisplayServer::WindowFlags(p_flag), p_enabled, window_id); } -#else - DisplayServer::get_singleton()->window_set_flag(DisplayServer::WindowFlags(p_flag), p_enabled, window_id); -#endif } } bool Window::get_flag(Flags p_flag) const { ERR_FAIL_INDEX_V(p_flag, FLAG_MAX, false); if (window_id != DisplayServer::INVALID_WINDOW_ID) { -#ifdef TOOLS_ENABLED - if ((p_flag != FLAG_POPUP) || !(Engine::get_singleton()->is_editor_hint() && get_tree()->get_edited_scene_root() && (get_tree()->get_edited_scene_root()->is_ancestor_of(this) || get_tree()->get_edited_scene_root() == this))) { + if (!is_in_edited_scene_root()) { flags[p_flag] = DisplayServer::get_singleton()->window_get_flag(DisplayServer::WindowFlags(p_flag), window_id); } -#else - flags[p_flag] = DisplayServer::get_singleton()->window_get_flag(DisplayServer::WindowFlags(p_flag), window_id); -#endif } return flags[p_flag]; } @@ -232,6 +224,14 @@ bool Window::is_embedded() const { return _get_embedder() != nullptr; } +bool Window::is_in_edited_scene_root() const { +#ifdef TOOLS_ENABLED + return (Engine::get_singleton()->is_editor_hint() && get_tree()->get_edited_scene_root() && (get_tree()->get_edited_scene_root()->is_ancestor_of(this) || get_tree()->get_edited_scene_root() == this)); +#else + return false; +#endif +} + void Window::_make_window() { ERR_FAIL_COND(window_id != DisplayServer::INVALID_WINDOW_ID); @@ -259,15 +259,12 @@ void Window::_make_window() { #endif DisplayServer::get_singleton()->window_set_title(tr_title, window_id); DisplayServer::get_singleton()->window_attach_instance_id(get_instance_id(), window_id); -#ifdef TOOLS_ENABLED - if (!(Engine::get_singleton()->is_editor_hint() && get_tree()->get_edited_scene_root() && (get_tree()->get_edited_scene_root()->is_ancestor_of(this) || get_tree()->get_edited_scene_root() == this))) { - DisplayServer::get_singleton()->window_set_exclusive(window_id, exclusive); - } else { + + if (is_in_edited_scene_root()) { DisplayServer::get_singleton()->window_set_exclusive(window_id, false); + } else { + DisplayServer::get_singleton()->window_set_exclusive(window_id, exclusive); } -#else - DisplayServer::get_singleton()->window_set_exclusive(window_id, exclusive); -#endif _update_window_size(); @@ -389,9 +386,24 @@ void Window::_event_callback(DisplayServer::WindowEvent p_event) { _propagate_window_notification(this, NOTIFICATION_WM_DPI_CHANGE); emit_signal(SNAME("dpi_changed")); } break; + case DisplayServer::WINDOW_EVENT_TITLEBAR_CHANGE: { + emit_signal(SNAME("titlebar_changed")); + } break; } } +void Window::update_mouse_cursor_shape() { + // The default shape is set in Viewport::_gui_input_event. To instantly + // see the shape in the viewport we need to trigger a mouse motion event. + Ref<InputEventMouseMotion> mm; + Vector2 pos = get_mouse_position(); + Transform2D xform = get_global_canvas_transform().affine_inverse(); + mm.instantiate(); + mm->set_position(pos); + mm->set_global_position(xform.xform(pos)); + push_input(mm); +} + void Window::show() { set_visible(true); } @@ -450,14 +462,10 @@ void Window::set_visible(bool p_visible) { //update transient exclusive if (transient_parent) { if (exclusive && visible) { -#ifdef TOOLS_ENABLED - if (!(Engine::get_singleton()->is_editor_hint() && get_tree()->get_edited_scene_root() && (get_tree()->get_edited_scene_root()->is_ancestor_of(this) || get_tree()->get_edited_scene_root() == this))) { + if (!is_in_edited_scene_root()) { ERR_FAIL_COND_MSG(transient_parent->exclusive_child && transient_parent->exclusive_child != this, "Transient parent has another exclusive child."); transient_parent->exclusive_child = this; } -#else - transient_parent->exclusive_child = this; -#endif } else { if (transient_parent->exclusive_child == this) { transient_parent->exclusive_child = nullptr; @@ -504,13 +512,9 @@ void Window::_make_transient() { window->transient_children.insert(this); if (is_inside_tree() && is_visible() && exclusive) { if (transient_parent->exclusive_child == nullptr) { -#ifdef TOOLS_ENABLED - if (!(Engine::get_singleton()->is_editor_hint() && get_tree()->get_edited_scene_root() && (get_tree()->get_edited_scene_root()->is_ancestor_of(this) || get_tree()->get_edited_scene_root() == this))) { + if (!is_in_edited_scene_root()) { transient_parent->exclusive_child = this; } -#else - transient_parent->exclusive_child = this; -#endif } else if (transient_parent->exclusive_child != this) { ERR_PRINT("Making child transient exclusive, but parent has another exclusive child"); } @@ -553,27 +557,19 @@ void Window::set_exclusive(bool p_exclusive) { exclusive = p_exclusive; if (!embedder && window_id != DisplayServer::INVALID_WINDOW_ID) { -#ifdef TOOLS_ENABLED - if (!(Engine::get_singleton()->is_editor_hint() && get_tree()->get_edited_scene_root() && (get_tree()->get_edited_scene_root()->is_ancestor_of(this) || get_tree()->get_edited_scene_root() == this))) { - DisplayServer::get_singleton()->window_set_exclusive(window_id, exclusive); - } else { + if (is_in_edited_scene_root()) { DisplayServer::get_singleton()->window_set_exclusive(window_id, false); + } else { + DisplayServer::get_singleton()->window_set_exclusive(window_id, exclusive); } -#else - DisplayServer::get_singleton()->window_set_exclusive(window_id, exclusive); -#endif } if (transient_parent) { if (p_exclusive && is_inside_tree() && is_visible()) { ERR_FAIL_COND_MSG(transient_parent->exclusive_child && transient_parent->exclusive_child != this, "Transient parent has another exclusive child."); -#ifdef TOOLS_ENABLED - if (!(Engine::get_singleton()->is_editor_hint() && get_tree()->get_edited_scene_root() && (get_tree()->get_edited_scene_root()->is_ancestor_of(this) || get_tree()->get_edited_scene_root() == this))) { + if (!is_in_edited_scene_root()) { transient_parent->exclusive_child = this; } -#else - transient_parent->exclusive_child = this; -#endif } else { if (transient_parent->exclusive_child == this) { transient_parent->exclusive_child = nullptr; @@ -591,6 +587,18 @@ bool Window::is_visible() const { } void Window::_update_window_size() { + // Force window to respect size limitations of rendering server + RenderingServer *rendering_server = RenderingServer::get_singleton(); + if (rendering_server) { + Size2i max_window_size = rendering_server->get_maximum_viewport_size(); + + if (max_window_size != Size2i()) { + size = size.min(max_window_size); + min_size = min_size.min(max_window_size); + max_size = max_size.min(max_window_size); + } + } + Size2i size_limit; if (wrap_controls) { size_limit = get_contents_minimum_size(); @@ -636,9 +644,9 @@ void Window::_update_window_size() { DisplayServer::get_singleton()->window_set_min_size(Size2i(), window_id); } - DisplayServer::get_singleton()->window_set_size(size, window_id); DisplayServer::get_singleton()->window_set_max_size(max_size_valid ? max_size : Size2i(), window_id); DisplayServer::get_singleton()->window_set_min_size(size_limit, window_id); + DisplayServer::get_singleton()->window_set_size(size, window_id); } //update the viewport @@ -651,7 +659,7 @@ void Window::_update_viewport_size() { Size2i final_size; Size2i final_size_override; Rect2i attach_to_screen_rect(Point2i(), size); - Transform2D stretch_transform; + Transform2D stretch_transform_new; float font_oversampling = 1.0; if (content_scale_mode == CONTENT_SCALE_MODE_DISABLED || content_scale_size.x == 0 || content_scale_size.y == 0) { @@ -659,8 +667,8 @@ void Window::_update_viewport_size() { final_size = size; final_size_override = Size2(size) / content_scale_factor; - stretch_transform = Transform2D(); - stretch_transform.scale(Size2(content_scale_factor, content_scale_factor)); + stretch_transform_new = Transform2D(); + stretch_transform_new.scale(Size2(content_scale_factor, content_scale_factor)); } else { //actual screen video mode Size2 video_mode = size; @@ -737,7 +745,7 @@ void Window::_update_viewport_size() { font_oversampling = (screen_size.x / viewport_size.x) * content_scale_factor; Size2 scale = Vector2(screen_size) / Vector2(final_size_override); - stretch_transform.scale(scale); + stretch_transform_new.scale(scale); } break; case CONTENT_SCALE_MODE_VIEWPORT: { @@ -749,7 +757,7 @@ void Window::_update_viewport_size() { } bool allocate = is_inside_tree() && visible && (window_id != DisplayServer::INVALID_WINDOW_ID || embedder != nullptr); - _set_size(final_size, final_size_override, attach_to_screen_rect, stretch_transform, allocate); + _set_size(final_size, final_size_override, attach_to_screen_rect, stretch_transform_new, allocate); if (window_id != DisplayServer::INVALID_WINDOW_ID) { RenderingServer::get_singleton()->viewport_attach_to_screen(get_viewport_rid(), attach_to_screen_rect, window_id); @@ -986,18 +994,6 @@ DisplayServer::WindowID Window::get_window_id() const { return window_id; } -void Window::warp_mouse(const Vector2 &p_position) { - Transform2D xform = get_screen_transform(); - Vector2 gpos = xform.xform(p_position); - - if (transient_parent && !transient_parent->is_embedding_subwindows()) { - Transform2D window_trans = Transform2D().translated(get_position() + (transient_parent->get_visible_rect().size - transient_parent->get_real_size())); - gpos = window_trans.xform(gpos); - } - - Input::get_singleton()->warp_mouse(gpos); -} - void Window::set_wrap_controls(bool p_enable) { wrap_controls = p_enable; if (wrap_controls) { @@ -1153,7 +1149,7 @@ void Window::popup_centered_clamped(const Size2i &p_size, float p_fallback_ratio Rect2 parent_rect; if (is_embedded()) { - parent_rect = get_parent_viewport()->get_visible_rect(); + parent_rect = _get_embedder()->get_visible_rect(); } else { DisplayServer::WindowID parent_id = get_parent_visible_window()->get_window_id(); int parent_screen = DisplayServer::get_singleton()->window_get_current_screen(parent_id); @@ -1179,7 +1175,7 @@ void Window::popup_centered(const Size2i &p_minsize) { Rect2 parent_rect; if (is_embedded()) { - parent_rect = get_parent_viewport()->get_visible_rect(); + parent_rect = _get_embedder()->get_visible_rect(); } else { DisplayServer::WindowID parent_id = get_parent_visible_window()->get_window_id(); int parent_screen = DisplayServer::get_singleton()->window_get_current_screen(parent_id); @@ -1207,7 +1203,7 @@ void Window::popup_centered_ratio(float p_ratio) { Rect2 parent_rect; if (is_embedded()) { - parent_rect = get_parent_viewport()->get_visible_rect(); + parent_rect = _get_embedder()->get_visible_rect(); } else { DisplayServer::WindowID parent_id = get_parent_visible_window()->get_window_id(); int parent_screen = DisplayServer::get_singleton()->window_get_current_screen(parent_id); @@ -1260,6 +1256,19 @@ void Window::popup(const Rect2i &p_screen_rect) { set_transient(true); set_visible(true); + + Rect2i parent_rect; + if (is_embedded()) { + parent_rect = _get_embedder()->get_visible_rect(); + } else { + int screen_id = DisplayServer::get_singleton()->window_get_current_screen(get_window_id()); + parent_rect = DisplayServer::get_singleton()->screen_get_usable_rect(screen_id); + } + if (parent_rect != Rect2i() && !parent_rect.intersects(Rect2i(position, size))) { + ERR_PRINT(vformat("Window %d spawned at invalid position: %s.", get_window_id(), position)); + set_position((parent_rect.size - size) / 2); + } + _post_popup(); notification(NOTIFICATION_POST_POPUP); } @@ -1282,17 +1291,17 @@ bool Window::has_focus() const { Rect2i Window::get_usable_parent_rect() const { ERR_FAIL_COND_V(!is_inside_tree(), Rect2()); - Rect2i parent; + Rect2i parent_rect; if (is_embedded()) { - parent = _get_embedder()->get_visible_rect(); + parent_rect = _get_embedder()->get_visible_rect(); } else { const Window *w = is_visible() ? this : get_parent_visible_window(); //find a parent that can contain us ERR_FAIL_COND_V(!w, Rect2()); - parent = DisplayServer::get_singleton()->screen_get_usable_rect(DisplayServer::get_singleton()->window_get_current_screen(w->get_window_id())); + parent_rect = DisplayServer::get_singleton()->screen_get_usable_rect(DisplayServer::get_singleton()->window_get_current_screen(w->get_window_id())); } - return parent; + return parent_rect; } void Window::add_child_notify(Node *p_child) { @@ -1560,9 +1569,9 @@ Window::LayoutDirection Window::get_layout_direction() const { bool Window::is_layout_rtl() const { if (layout_dir == LAYOUT_DIRECTION_INHERITED) { - Window *parent = Object::cast_to<Window>(get_parent()); - if (parent) { - return parent->is_layout_rtl(); + Window *parent_w = Object::cast_to<Window>(get_parent()); + if (parent_w) { + return parent_w->is_layout_rtl(); } else { if (GLOBAL_GET(SNAME("internationalization/rendering/force_right_to_left_layout_direction"))) { return true; @@ -1624,7 +1633,7 @@ void Window::_validate_property(PropertyInfo &p_property) const { } Transform2D Window::get_screen_transform() const { - Transform2D embedder_transform = Transform2D(); + Transform2D embedder_transform; if (_get_embedder()) { embedder_transform.translate_local(get_position()); embedder_transform = _get_embedder()->get_screen_transform() * embedder_transform; @@ -1794,6 +1803,7 @@ void Window::_bind_methods() { ADD_SIGNAL(MethodInfo("visibility_changed")); ADD_SIGNAL(MethodInfo("about_to_popup")); ADD_SIGNAL(MethodInfo("theme_changed")); + ADD_SIGNAL(MethodInfo("titlebar_changed")); BIND_CONSTANT(NOTIFICATION_VISIBILITY_CHANGED); BIND_CONSTANT(NOTIFICATION_THEME_CHANGED); @@ -1830,6 +1840,11 @@ void Window::_bind_methods() { } Window::Window() { + RenderingServer *rendering_server = RenderingServer::get_singleton(); + if (rendering_server) { + max_size = rendering_server->get_maximum_viewport_size(); + } + theme_owner = memnew(ThemeOwner); RS::get_singleton()->viewport_set_update_mode(get_viewport_rid(), RS::VIEWPORT_UPDATE_DISABLED); } diff --git a/scene/main/window.h b/scene/main/window.h index 8113117103..03597b309a 100644 --- a/scene/main/window.h +++ b/scene/main/window.h @@ -220,6 +220,8 @@ public: void set_visible(bool p_visible); bool is_visible() const; + void update_mouse_cursor_shape(); + void show(); void hide(); @@ -232,6 +234,8 @@ public: void set_clamp_to_embedder(bool p_enable); bool is_clamped_to_embedder() const; + bool is_in_edited_scene_root() const; + bool can_draw() const; void set_ime_active(bool p_active); @@ -254,8 +258,6 @@ public: void set_use_font_oversampling(bool p_oversampling); bool is_using_font_oversampling() const; - void warp_mouse(const Vector2 &p_position) override; - void set_wrap_controls(bool p_enable); bool is_wrapping_controls() const; void child_controls_changed(); |