diff options
Diffstat (limited to 'scene/main')
| -rw-r--r-- | scene/main/canvas_item.cpp | 129 | ||||
| -rw-r--r-- | scene/main/canvas_item.h | 36 | ||||
| -rw-r--r-- | scene/main/canvas_layer.cpp | 13 | ||||
| -rw-r--r-- | scene/main/canvas_layer.h | 2 | ||||
| -rw-r--r-- | scene/main/http_request.cpp | 149 | ||||
| -rw-r--r-- | scene/main/http_request.h | 14 | ||||
| -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 | 84 | ||||
| -rw-r--r-- | scene/main/multiplayer_peer.cpp | 127 | ||||
| -rw-r--r-- | scene/main/multiplayer_peer.h | 80 | ||||
| -rw-r--r-- | scene/main/node.cpp | 245 | ||||
| -rw-r--r-- | scene/main/node.h | 30 | ||||
| -rw-r--r-- | scene/main/scene_tree.cpp | 329 | ||||
| -rw-r--r-- | scene/main/scene_tree.h | 27 | ||||
| -rw-r--r-- | scene/main/shader_globals_override.cpp | 18 | ||||
| -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 | 381 | ||||
| -rw-r--r-- | scene/main/viewport.h | 25 | ||||
| -rw-r--r-- | scene/main/window.cpp | 421 | ||||
| -rw-r--r-- | scene/main/window.h | 34 |
24 files changed, 1176 insertions, 984 deletions
diff --git a/scene/main/canvas_item.cpp b/scene/main/canvas_item.cpp index ce204c6aeb..7bcd4721fc 100644 --- a/scene/main/canvas_item.cpp +++ b/scene/main/canvas_item.cpp @@ -88,7 +88,7 @@ void CanvasItem::_handle_visibility_change(bool p_visible) { notification(NOTIFICATION_VISIBILITY_CHANGED); if (p_visible) { - update(); + queue_redraw(); } else { emit_signal(SceneStringNames::get_singleton()->hidden); } @@ -121,7 +121,7 @@ CanvasItem *CanvasItem::get_current_item_drawn() { return current_item_drawn; } -void CanvasItem::_update_callback() { +void CanvasItem::_redraw_callback() { if (!is_inside_tree()) { pending_update = false; return; @@ -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,10 +237,11 @@ 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; - update(); + queue_redraw(); notification(NOTIFICATION_ENTER_CANVAS); } @@ -338,6 +337,7 @@ void CanvasItem::_notification(int p_what) { } if (window) { window->disconnect(SceneStringNames::get_singleton()->visibility_changed, callable_mp(this, &CanvasItem::_window_visibility_changed)); + window = nullptr; } global_invalid = true; parent_visible_in_tree = false; @@ -355,7 +355,7 @@ void CanvasItem::_window_visibility_changed() { } } -void CanvasItem::update() { +void CanvasItem::queue_redraw() { if (!is_inside_tree()) { return; } @@ -365,7 +365,14 @@ void CanvasItem::update() { pending_update = true; - MessageQueue::get_singleton()->push_call(this, SNAME("_update_callback")); + MessageQueue::get_singleton()->push_callable(callable_mp(this, &CanvasItem::_redraw_callback)); +} + +void CanvasItem::move_to_front() { + if (!get_parent()) { + return; + } + get_parent()->move_child(this, -1); } void CanvasItem::set_modulate(const Color &p_modulate) { @@ -438,7 +445,7 @@ int CanvasItem::get_light_mask() const { void CanvasItem::item_rect_changed(bool p_size_changed) { if (p_size_changed) { - update(); + queue_redraw(); } emit_signal(SceneStringNames::get_singleton()->item_rect_changed); } @@ -587,6 +594,12 @@ void CanvasItem::draw_msdf_texture_rect_region(const Ref<Texture2D> &p_texture, RenderingServer::get_singleton()->canvas_item_add_msdf_texture_rect_region(canvas_item, p_rect, p_texture->get_rid(), p_src_rect, p_modulate, p_outline, p_pixel_range); } +void CanvasItem::draw_lcd_texture_rect_region(const Ref<Texture2D> &p_texture, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate) { + ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal."); + ERR_FAIL_COND(p_texture.is_null()); + RenderingServer::get_singleton()->canvas_item_add_lcd_texture_rect_region(canvas_item, p_rect, p_texture->get_rid(), p_src_rect, p_modulate); +} + void CanvasItem::draw_style_box(const Ref<StyleBox> &p_style_box, const Rect2 &p_rect) { ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal."); @@ -861,7 +874,6 @@ void CanvasItem::force_update_transform() { void CanvasItem::_bind_methods() { ClassDB::bind_method(D_METHOD("_top_level_raise_self"), &CanvasItem::_top_level_raise_self); - ClassDB::bind_method(D_METHOD("_update_callback"), &CanvasItem::_update_callback); #ifdef TOOLS_ENABLED ClassDB::bind_method(D_METHOD("_edit_set_state", "state"), &CanvasItem::_edit_set_state); @@ -890,7 +902,8 @@ void CanvasItem::_bind_methods() { ClassDB::bind_method(D_METHOD("show"), &CanvasItem::show); ClassDB::bind_method(D_METHOD("hide"), &CanvasItem::hide); - ClassDB::bind_method(D_METHOD("update"), &CanvasItem::update); + ClassDB::bind_method(D_METHOD("queue_redraw"), &CanvasItem::queue_redraw); + ClassDB::bind_method(D_METHOD("move_to_front"), &CanvasItem::move_to_front); ClassDB::bind_method(D_METHOD("set_as_top_level", "enable"), &CanvasItem::set_as_top_level); ClassDB::bind_method(D_METHOD("is_set_as_top_level"), &CanvasItem::is_set_as_top_level); @@ -919,6 +932,7 @@ void CanvasItem::_bind_methods() { ClassDB::bind_method(D_METHOD("draw_texture_rect", "texture", "rect", "tile", "modulate", "transpose"), &CanvasItem::draw_texture_rect, DEFVAL(Color(1, 1, 1, 1)), DEFVAL(false)); ClassDB::bind_method(D_METHOD("draw_texture_rect_region", "texture", "rect", "src_rect", "modulate", "transpose", "clip_uv"), &CanvasItem::draw_texture_rect_region, DEFVAL(Color(1, 1, 1, 1)), DEFVAL(false), DEFVAL(true)); ClassDB::bind_method(D_METHOD("draw_msdf_texture_rect_region", "texture", "rect", "src_rect", "modulate", "outline", "pixel_range"), &CanvasItem::draw_msdf_texture_rect_region, DEFVAL(Color(1, 1, 1, 1)), DEFVAL(0.0), DEFVAL(4.0)); + ClassDB::bind_method(D_METHOD("draw_lcd_texture_rect_region", "texture", "rect", "src_rect", "modulate"), &CanvasItem::draw_lcd_texture_rect_region, DEFVAL(Color(1, 1, 1, 1))); ClassDB::bind_method(D_METHOD("draw_style_box", "style_box", "rect"), &CanvasItem::draw_style_box); ClassDB::bind_method(D_METHOD("draw_primitive", "points", "colors", "uvs", "texture", "width"), &CanvasItem::draw_primitive, DEFVAL(Ref<Texture2D>()), DEFVAL(1.0)); ClassDB::bind_method(D_METHOD("draw_polygon", "points", "colors", "uvs", "texture"), &CanvasItem::draw_polygon, DEFVAL(PackedVector2Array()), DEFVAL(Ref<Texture2D>())); @@ -965,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); @@ -982,11 +1001,12 @@ 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 Aniso.,Linear Mipmap Aniso."), "set_texture_filter", "get_texture_filter"); + 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"); ADD_PROPERTY(PropertyInfo(Variant::INT, "texture_repeat", PROPERTY_HINT_ENUM, "Inherit,Disabled,Enabled,Mirror"), "set_texture_repeat", "get_texture_repeat"); ADD_GROUP("Material", ""); @@ -1020,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 { @@ -1077,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; } @@ -1092,8 +1140,16 @@ 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); - update(); + queue_redraw(); if (p_propagate) { for (CanvasItem *E : children_items) { @@ -1118,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; } @@ -1133,8 +1189,16 @@ 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); - update(); + queue_redraw(); if (p_propagate) { for (CanvasItem *E : children_items) { if (!E->top_level && E->texture_repeat == TEXTURE_REPEAT_PARENT_NODE) { @@ -1154,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(); @@ -1326,7 +1403,7 @@ void CanvasTexture::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::COLOR, "specular_color", PROPERTY_HINT_COLOR_NO_ALPHA), "set_specular_color", "get_specular_color"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "specular_shininess", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_specular_shininess", "get_specular_shininess"); ADD_GROUP("Texture", "texture_"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "texture_filter", PROPERTY_HINT_ENUM, "Inherit,Nearest,Linear,Nearest Mipmap,Linear Mipmap,Nearest Mipmap Aniso.,Linear Mipmap Aniso."), "set_texture_filter", "get_texture_filter"); + 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"); ADD_PROPERTY(PropertyInfo(Variant::INT, "texture_repeat", PROPERTY_HINT_ENUM, "Inherit,Disabled,Enabled,Mirror"), "set_texture_repeat", "get_texture_repeat"); } diff --git a/scene/main/canvas_item.h b/scene/main/canvas_item.h index 38e0be1683..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; @@ -110,7 +120,7 @@ private: void _propagate_visibility_changed(bool p_parent_visible_in_tree); void _handle_visibility_change(bool p_visible); - void _update_callback(); + void _redraw_callback(); void _enter_canvas(); void _exit_canvas(); @@ -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: @@ -197,10 +209,11 @@ public: void show(); void hide(); - void update(); + 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; @@ -211,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); @@ -226,6 +245,7 @@ public: void draw_texture_rect(const Ref<Texture2D> &p_texture, const Rect2 &p_rect, bool p_tile = false, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false); void draw_texture_rect_region(const Ref<Texture2D> &p_texture, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false, bool p_clip_uv = false); void draw_msdf_texture_rect_region(const Ref<Texture2D> &p_texture, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate = Color(1, 1, 1), double p_outline = 0.0, double p_pixel_range = 4.0); + void draw_lcd_texture_rect_region(const Ref<Texture2D> &p_texture, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate = Color(1, 1, 1)); void draw_style_box(const Ref<StyleBox> &p_style_box, const Rect2 &p_rect); void draw_primitive(const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs, Ref<Texture2D> p_texture = Ref<Texture2D>(), real_t p_width = 1); void draw_polygon(const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs = Vector<Point2>(), Ref<Texture2D> p_texture = Ref<Texture2D>()); @@ -308,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); }; @@ -319,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 3a071ef542..be5788739b 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(); } } @@ -286,9 +287,9 @@ void CanvasLayer::_update_follow_viewport(bool p_force_exit) { } } -void CanvasLayer::_validate_property(PropertyInfo &property) const { - if (!follow_viewport && property.name == "follow_viewport_scale") { - property.usage = PROPERTY_USAGE_NO_EDITOR; +void CanvasLayer::_validate_property(PropertyInfo &p_property) const { + if (!follow_viewport && p_property.name == "follow_viewport_scale") { + p_property.usage = PROPERTY_USAGE_NO_EDITOR; } } @@ -329,14 +330,14 @@ void CanvasLayer::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "visible"), "set_visible", "is_visible"); ADD_GROUP("Transform", ""); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "offset", PROPERTY_HINT_NONE, "suffix:px"), "set_offset", "get_offset"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "rotation", PROPERTY_HINT_RANGE, "-1080,1080,0.1,or_lesser,or_greater,radians"), "set_rotation", "get_rotation"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "rotation", PROPERTY_HINT_RANGE, "-1080,1080,0.1,or_less,or_greater,radians"), "set_rotation", "get_rotation"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "scale", PROPERTY_HINT_LINK), "set_scale", "get_scale"); ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM2D, "transform", PROPERTY_HINT_NONE, "suffix:px"), "set_transform", "get_transform"); ADD_GROUP("", ""); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "custom_viewport", PROPERTY_HINT_RESOURCE_TYPE, "Viewport", PROPERTY_USAGE_NONE), "set_custom_viewport", "get_custom_viewport"); ADD_GROUP("Follow Viewport", "follow_viewport"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "follow_viewport_enable"), "set_follow_viewport", "is_following_viewport"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "follow_viewport_scale", PROPERTY_HINT_RANGE, "0.001,1000,0.001,or_greater,or_lesser"), "set_follow_viewport_scale", "get_follow_viewport_scale"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "follow_viewport_enabled"), "set_follow_viewport", "is_following_viewport"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "follow_viewport_scale", PROPERTY_HINT_RANGE, "0.001,1000,0.001,or_greater,or_less"), "set_follow_viewport_scale", "get_follow_viewport_scale"); ADD_SIGNAL(MethodInfo("visibility_changed")); } diff --git a/scene/main/canvas_layer.h b/scene/main/canvas_layer.h index 2493675b31..74b5ebd453 100644 --- a/scene/main/canvas_layer.h +++ b/scene/main/canvas_layer.h @@ -64,7 +64,7 @@ class CanvasLayer : public Node { protected: void _notification(int p_what); static void _bind_methods(); - void _validate_property(PropertyInfo &property) const override; + void _validate_property(PropertyInfo &p_property) const; public: void set_layer(int p_xform); diff --git a/scene/main/http_request.cpp b/scene/main/http_request.cpp index 9a23bc65bf..2c395ec07d 100644 --- a/scene/main/http_request.cpp +++ b/scene/main/http_request.cpp @@ -32,15 +32,12 @@ #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_ssl, validate_ssl); + return client->connect_to_host(url, port, use_tls, validate_tls); } Error HTTPRequest::_parse_url(const String &p_url) { - use_ssl = false; + use_tls = false; request_string = ""; port = 80; request_sent = false; @@ -48,18 +45,19 @@ 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; Error err = p_url.parse_url(scheme, url, port, request_string); ERR_FAIL_COND_V_MSG(err != OK, err, "Error parsing URL: " + p_url + "."); if (scheme == "https://") { - use_ssl = true; + use_tls = true; } else if (scheme != "http://") { ERR_FAIL_V_MSG(ERR_INVALID_PARAMETER, "Invalid URL scheme: " + scheme + "."); } if (port == 0) { - port = use_ssl ? 443 : 80; + port = use_tls ? 443 : 80; } if (request_string.is_empty()) { request_string = "/"; @@ -98,7 +96,7 @@ String HTTPRequest::get_header_value(const PackedStringArray &p_headers, const S return value; } -Error HTTPRequest::request(const String &p_url, const Vector<String> &p_custom_headers, bool p_ssl_validate_domain, HTTPClient::Method p_method, const String &p_request_data) { +Error HTTPRequest::request(const String &p_url, const Vector<String> &p_custom_headers, bool p_tls_validate_domain, HTTPClient::Method p_method, const String &p_request_data) { // Copy the string into a raw buffer. Vector<uint8_t> raw_data; @@ -110,10 +108,10 @@ Error HTTPRequest::request(const String &p_url, const Vector<String> &p_custom_h memcpy(w, charstr.ptr(), len); } - return request_raw(p_url, p_custom_headers, p_ssl_validate_domain, p_method, raw_data); + return request_raw(p_url, p_custom_headers, p_tls_validate_domain, p_method, raw_data); } -Error HTTPRequest::request_raw(const String &p_url, const Vector<String> &p_custom_headers, bool p_ssl_validate_domain, HTTPClient::Method p_method, const Vector<uint8_t> &p_request_data_raw) { +Error HTTPRequest::request_raw(const String &p_url, const Vector<String> &p_custom_headers, bool p_tls_validate_domain, HTTPClient::Method p_method, const Vector<uint8_t> &p_request_data_raw) { ERR_FAIL_COND_V(!is_inside_tree(), ERR_UNCONFIGURED); ERR_FAIL_COND_V_MSG(requesting, ERR_BUSY, "HTTPRequest is processing a request. Wait for completion or cancel it before attempting a new one."); @@ -129,7 +127,7 @@ Error HTTPRequest::request_raw(const String &p_url, const Vector<String> &p_cust return err; } - validate_ssl = p_ssl_validate_domain; + validate_tls = p_tls_validate_domain; headers = p_custom_headers; @@ -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() * 2); + } else if (content_encoding == "deflate") { + decompressor.instantiate(); + decompressor->start_decompression(true, get_download_chunk_size() * 2); + } + 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; } } @@ -375,14 +391,33 @@ bool HTTPRequest::_update_connection() { } PackedByteArray chunk = client->read_response_body_chunk(); + downloaded.add(chunk.size()); + + // Decompress chunk if needed. + if (decompressor.is_valid()) { + Error err = decompressor->put_data(chunk.ptr(), chunk.size()); + if (err == OK) { + chunk.resize(decompressor->get_available_bytes()); + err = decompressor->get_data(chunk.ptrw(), chunk.size()); + } + if (err != OK) { + _defer_done(RESULT_BODY_DECOMPRESS_FAILED, response_code, response_headers, PackedByteArray()); + return true; + } + } + 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 +425,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 +440,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_SSL_HANDSHAKE_ERROR: { - call_deferred(SNAME("_request_done"), RESULT_SSL_HANDSHAKE_ERROR, 0, PackedStringArray(), PackedByteArray()); + case HTTPClient::STATUS_TLS_HANDSHAKE_ERROR: { + _defer_done(RESULT_TLS_HANDSHAKE_ERROR, 0, PackedStringArray(), PackedByteArray()); return true; } break; } @@ -422,41 +452,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,12 +568,12 @@ 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() { - ClassDB::bind_method(D_METHOD("request", "url", "custom_headers", "ssl_validate_domain", "method", "request_data"), &HTTPRequest::request, DEFVAL(PackedStringArray()), DEFVAL(true), DEFVAL(HTTPClient::METHOD_GET), DEFVAL(String())); - ClassDB::bind_method(D_METHOD("request_raw", "url", "custom_headers", "ssl_validate_domain", "method", "request_data_raw"), &HTTPRequest::request_raw, DEFVAL(PackedStringArray()), DEFVAL(true), DEFVAL(HTTPClient::METHOD_GET), DEFVAL(PackedByteArray())); + ClassDB::bind_method(D_METHOD("request", "url", "custom_headers", "tls_validate_domain", "method", "request_data"), &HTTPRequest::request, DEFVAL(PackedStringArray()), DEFVAL(true), DEFVAL(HTTPClient::METHOD_GET), DEFVAL(String())); + ClassDB::bind_method(D_METHOD("request_raw", "url", "custom_headers", "tls_validate_domain", "method", "request_data_raw"), &HTTPRequest::request_raw, DEFVAL(PackedStringArray()), DEFVAL(true), DEFVAL(HTTPClient::METHOD_GET), DEFVAL(PackedByteArray())); ClassDB::bind_method(D_METHOD("cancel_request"), &HTTPRequest::cancel_request); ClassDB::bind_method(D_METHOD("get_http_client_status"), &HTTPRequest::get_http_client_status); @@ -594,7 +596,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); @@ -621,7 +622,7 @@ void HTTPRequest::_bind_methods() { BIND_ENUM_CONSTANT(RESULT_CANT_CONNECT); BIND_ENUM_CONSTANT(RESULT_CANT_RESOLVE); BIND_ENUM_CONSTANT(RESULT_CONNECTION_ERROR); - BIND_ENUM_CONSTANT(RESULT_SSL_HANDSHAKE_ERROR); + BIND_ENUM_CONSTANT(RESULT_TLS_HANDSHAKE_ERROR); BIND_ENUM_CONSTANT(RESULT_NO_RESPONSE); BIND_ENUM_CONSTANT(RESULT_BODY_SIZE_LIMIT_EXCEEDED); BIND_ENUM_CONSTANT(RESULT_BODY_DECOMPRESS_FAILED); diff --git a/scene/main/http_request.h b/scene/main/http_request.h index 4b32188377..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" @@ -48,7 +49,7 @@ public: RESULT_CANT_CONNECT, RESULT_CANT_RESOLVE, RESULT_CONNECTION_ERROR, - RESULT_SSL_HANDSHAKE_ERROR, + RESULT_TLS_HANDSHAKE_ERROR, RESULT_NO_RESPONSE, RESULT_BODY_SIZE_LIMIT_EXCEEDED, RESULT_BODY_DECOMPRESS_FAILED, @@ -67,8 +68,8 @@ private: String url; int port = 80; Vector<String> headers; - bool validate_ssl = false; - bool use_ssl = false; + bool validate_tls = false; + bool use_tls = false; HTTPClient::Method method; Vector<uint8_t> request_data; @@ -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); @@ -121,8 +125,8 @@ protected: static void _bind_methods(); public: - Error request(const String &p_url, const Vector<String> &p_custom_headers = Vector<String>(), bool p_ssl_validate_domain = true, HTTPClient::Method p_method = HTTPClient::METHOD_GET, const String &p_request_data = ""); //connects to a full url and perform request - Error request_raw(const String &p_url, const Vector<String> &p_custom_headers = Vector<String>(), bool p_ssl_validate_domain = true, HTTPClient::Method p_method = HTTPClient::METHOD_GET, const Vector<uint8_t> &p_request_data_raw = Vector<uint8_t>()); //connects to a full url and perform request + Error request(const String &p_url, const Vector<String> &p_custom_headers = Vector<String>(), bool p_tls_validate_domain = true, HTTPClient::Method p_method = HTTPClient::METHOD_GET, const String &p_request_data = ""); //connects to a full url and perform request + Error request_raw(const String &p_url, const Vector<String> &p_custom_headers = Vector<String>(), bool p_tls_validate_domain = true, HTTPClient::Method p_method = HTTPClient::METHOD_GET, const Vector<uint8_t> &p_request_data_raw = Vector<uint8_t>()); //connects to a full url and perform request void cancel_request(); HTTPClient::Status get_http_client_status() const; 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 95574042a8..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)); @@ -59,18 +59,18 @@ Ref<MultiplayerAPI> MultiplayerAPI::create_default_interface() { // The variant is compressed and encoded; The first byte contains all the meta // information and the format is: -// - The first LSB 5 bits are used for the variant type. +// - The first LSB 6 bits are used for the variant type. // - The next two bits are used to store the encoding mode. -// - The most significant is used to store the boolean value. -#define VARIANT_META_TYPE_MASK 0x1F -#define VARIANT_META_EMODE_MASK 0x60 +// - Boolean values uses the encoding mode to store the value. +#define VARIANT_META_TYPE_MASK 0x3F +#define VARIANT_META_EMODE_MASK 0xC0 #define VARIANT_META_BOOL_MASK 0x80 -#define ENCODE_8 0 << 5 -#define ENCODE_16 1 << 5 -#define ENCODE_32 2 << 5 -#define ENCODE_64 3 << 5 +#define ENCODE_8 0 << 6 +#define ENCODE_16 1 << 6 +#define ENCODE_32 2 << 6 +#define ENCODE_64 3 << 6 Error MultiplayerAPI::encode_and_compress_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bool p_allow_object_decoding) { - // Unreachable because `VARIANT_MAX` == 27 and `ENCODE_VARIANT_MASK` == 31 + // Unreachable because `VARIANT_MAX` == 38 and `ENCODE_VARIANT_MASK` == 77 CRASH_COND(p_variant.get_type() > VARIANT_META_TYPE_MASK); uint8_t *buf = r_buffer; @@ -80,9 +80,9 @@ Error MultiplayerAPI::encode_and_compress_variant(const Variant &p_variant, uint switch (p_variant.get_type()) { case Variant::BOOL: { if (buf) { - // We still have 1 free bit in the meta, so let's use it. + // We don't use encode_mode for booleans, so we can use it to store the value. buf[0] = (p_variant.operator bool()) ? (1 << 7) : 0; - buf[0] |= encode_mode | p_variant.get_type(); + buf[0] |= p_variant.get_type(); } r_len += 1; } break; @@ -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 aad5baccab..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,26 +123,14 @@ 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")); } /*************/ -int MultiplayerPeerExtension::get_available_packet_count() const { - int count; - if (GDVIRTUAL_CALL(_get_available_packet_count, count)) { - return count; - } - WARN_PRINT_ONCE("MultiplayerPeerExtension::_get_available_packet_count is unimplemented!"); - return -1; -} - Error MultiplayerPeerExtension::get_packet(const uint8_t **r_buffer, int &r_buffer_size) { - int err; + Error err; if (GDVIRTUAL_CALL(_get_packet, r_buffer, &r_buffer_size, err)) { - return (Error)err; + return err; } if (GDVIRTUAL_IS_OVERRIDDEN(_get_packet_script)) { if (!GDVIRTUAL_CALL(_get_packet_script, script_buffer)) { @@ -153,9 +151,9 @@ Error MultiplayerPeerExtension::get_packet(const uint8_t **r_buffer, int &r_buff } Error MultiplayerPeerExtension::put_packet(const uint8_t *p_buffer, int p_buffer_size) { - int err; + Error err; if (GDVIRTUAL_CALL(_put_packet, p_buffer, p_buffer_size, err)) { - return (Error)err; + return err; } if (GDVIRTUAL_IS_OVERRIDDEN(_put_packet_script)) { PackedByteArray a; @@ -171,87 +169,6 @@ Error MultiplayerPeerExtension::put_packet(const uint8_t *p_buffer, int p_buffer return FAILED; } -int MultiplayerPeerExtension::get_max_packet_size() const { - int size; - if (GDVIRTUAL_CALL(_get_max_packet_size, size)) { - return size; - } - WARN_PRINT_ONCE("MultiplayerPeerExtension::_get_max_packet_size is unimplemented!"); - return 0; -} - -void MultiplayerPeerExtension::set_transfer_channel(int p_channel) { - if (GDVIRTUAL_CALL(_set_transfer_channel, p_channel)) { - return; - } - MultiplayerPeer::set_transfer_channel(p_channel); -} - -int MultiplayerPeerExtension::get_transfer_channel() const { - int channel; - if (GDVIRTUAL_CALL(_get_transfer_channel, channel)) { - return channel; - } - return MultiplayerPeer::get_transfer_channel(); -} - -void MultiplayerPeerExtension::set_transfer_mode(TransferMode p_mode) { - if (GDVIRTUAL_CALL(_set_transfer_mode, p_mode)) { - return; - } - MultiplayerPeer::set_transfer_mode(p_mode); -} - -MultiplayerPeer::TransferMode MultiplayerPeerExtension::get_transfer_mode() const { - int mode; - if (GDVIRTUAL_CALL(_get_transfer_mode, mode)) { - return (MultiplayerPeer::TransferMode)mode; - } - return MultiplayerPeer::get_transfer_mode(); -} - -void MultiplayerPeerExtension::set_target_peer(int p_peer_id) { - if (GDVIRTUAL_CALL(_set_target_peer, p_peer_id)) { - return; - } - WARN_PRINT_ONCE("MultiplayerPeerExtension::_set_target_peer is unimplemented!"); -} - -int MultiplayerPeerExtension::get_packet_peer() const { - int peer; - if (GDVIRTUAL_CALL(_get_packet_peer, peer)) { - return peer; - } - WARN_PRINT_ONCE("MultiplayerPeerExtension::_get_packet_peer is unimplemented!"); - return 0; -} - -bool MultiplayerPeerExtension::is_server() const { - bool server; - if (GDVIRTUAL_CALL(_is_server, server)) { - return server; - } - WARN_PRINT_ONCE("MultiplayerPeerExtension::_is_server is unimplemented!"); - return false; -} - -void MultiplayerPeerExtension::poll() { - int err; - if (GDVIRTUAL_CALL(_poll, err)) { - return; - } - WARN_PRINT_ONCE("MultiplayerPeerExtension::_poll is unimplemented!"); -} - -int MultiplayerPeerExtension::get_unique_id() const { - int id; - if (GDVIRTUAL_CALL(_get_unique_id, id)) { - return id; - } - WARN_PRINT_ONCE("MultiplayerPeerExtension::_get_unique_id is unimplemented!"); - return 0; -} - void MultiplayerPeerExtension::set_refuse_new_connections(bool p_enable) { if (GDVIRTUAL_CALL(_set_refuse_new_connections, p_enable)) { return; @@ -267,13 +184,12 @@ bool MultiplayerPeerExtension::is_refusing_new_connections() const { return MultiplayerPeer::is_refusing_new_connections(); } -MultiplayerPeer::ConnectionStatus MultiplayerPeerExtension::get_connection_status() const { - int status; - if (GDVIRTUAL_CALL(_get_connection_status, status)) { - return (ConnectionStatus)status; +bool MultiplayerPeerExtension::is_server_relay_supported() const { + bool can_relay; + if (GDVIRTUAL_CALL(_is_server_relay_supported, can_relay)) { + return can_relay; } - WARN_PRINT_ONCE("MultiplayerPeerExtension::_get_connection_status is unimplemented!"); - return CONNECTION_DISCONNECTED; + return MultiplayerPeer::is_server_relay_supported(); } void MultiplayerPeerExtension::_bind_methods() { @@ -296,8 +212,13 @@ 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); GDVIRTUAL_BIND(_get_connection_status); + + ADD_PROPERTY_DEFAULT("transfer_mode", TRANSFER_MODE_RELIABLE); + ADD_PROPERTY_DEFAULT("transfer_channel", 0); } diff --git a/scene/main/multiplayer_peer.h b/scene/main/multiplayer_peer.h index 8a012d7520..4b5909538e 100644 --- a/scene/main/multiplayer_peer.h +++ b/scene/main/multiplayer_peer.h @@ -33,6 +33,7 @@ #include "core/io/packet_peer.h" +#include "core/extension/ext_wrappers.gen.inc" #include "core/object/gdvirtual.gen.inc" #include "core/object/script_language.h" #include "core/variant/native_ptr.h" @@ -73,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; @@ -103,55 +110,42 @@ protected: PackedByteArray script_buffer; public: - /* PacketPeer */ - virtual int get_available_packet_count() const override; + /* PacketPeer extension */ virtual Error get_packet(const uint8_t **r_buffer, int &r_buffer_size) override; ///< buffer is GONE after next get_packet - virtual Error put_packet(const uint8_t *p_buffer, int p_buffer_size) override; - virtual int get_max_packet_size() const override; - - /* MultiplayerPeer */ - virtual void set_transfer_channel(int p_channel) override; - virtual int get_transfer_channel() const override; - virtual void set_transfer_mode(TransferMode p_mode) override; - virtual TransferMode get_transfer_mode() const override; - virtual void set_target_peer(int p_peer_id) override; - - virtual int get_packet_peer() const override; + GDVIRTUAL2R(Error, _get_packet, GDNativeConstPtr<const uint8_t *>, GDNativePtr<int>); + GDVIRTUAL0R(PackedByteArray, _get_packet_script); // For GDScript. - virtual bool is_server() const override; - - virtual void poll() override; + virtual Error put_packet(const uint8_t *p_buffer, int p_buffer_size) override; + GDVIRTUAL2R(Error, _put_packet, GDNativeConstPtr<const uint8_t>, int); + GDVIRTUAL1R(Error, _put_packet_script, PackedByteArray); // For GDScript. - virtual int get_unique_id() const override; + EXBIND0RC(int, get_available_packet_count); + EXBIND0RC(int, get_max_packet_size); + /* MultiplayerPeer extension */ virtual void set_refuse_new_connections(bool p_enable) override; - virtual bool is_refusing_new_connections() const override; + GDVIRTUAL1(_set_refuse_new_connections, bool); // Optional. - virtual ConnectionStatus get_connection_status() const override; - - /* PacketPeer GDExtension */ - GDVIRTUAL0RC(int, _get_available_packet_count); - GDVIRTUAL2R(int, _get_packet, GDNativeConstPtr<const uint8_t *>, GDNativePtr<int>); - GDVIRTUAL2R(int, _put_packet, GDNativeConstPtr<const uint8_t>, int); - GDVIRTUAL0RC(int, _get_max_packet_size); - - /* PacketPeer GDScript */ - GDVIRTUAL0R(PackedByteArray, _get_packet_script); - GDVIRTUAL1R(int, _put_packet_script, PackedByteArray); - - /* MultiplayerPeer GDExtension */ - GDVIRTUAL1(_set_transfer_channel, int); - GDVIRTUAL0RC(int, _get_transfer_channel); - GDVIRTUAL1(_set_transfer_mode, int); - GDVIRTUAL0RC(int, _get_transfer_mode); - GDVIRTUAL1(_set_target_peer, int); - GDVIRTUAL0RC(int, _get_packet_peer); - GDVIRTUAL0RC(bool, _is_server); - GDVIRTUAL0R(int, _poll); - GDVIRTUAL0RC(int, _get_unique_id); - GDVIRTUAL1(_set_refuse_new_connections, bool); - GDVIRTUAL0RC(bool, _is_refusing_new_connections); - GDVIRTUAL0RC(int, _get_connection_status); + 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); }; #endif // MULTIPLAYER_PEER_H diff --git a/scene/main/node.cpp b/scene/main/node.cpp index ea9788de27..5dd0911694 100644 --- a/scene/main/node.cpp +++ b/scene/main/node.cpp @@ -322,53 +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()) { - 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); + if (p_index < 0) { + p_index += data.internal_children_front; + } + 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()) { - 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); + if (p_index < 0) { + p_index += data.internal_children_back; + } + 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 { - 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); + if (p_index < 0) { + p_index += get_child_count(false); + } + 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) { +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 call_deferred(\"move_child\") instead (or \"popup\" 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(); @@ -377,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); @@ -389,21 +398,6 @@ void Node::_move_child(Node *p_child, int p_pos, bool p_ignore_end) { data.blocked--; } -void Node::raise() { - if (!data.parent) { - return; - } - - // Internal children move within a different index range. - if (_is_internal_front()) { - data.parent->move_child(this, data.parent->data.internal_children_front - 1); - } else if (_is_internal_back()) { - data.parent->move_child(this, data.parent->data.internal_children_back - 1); - } else { - data.parent->move_child(this, data.parent->get_child_count(false) - 1); - } -} - void Node::_propagate_groups_dirty() { for (const KeyValue<StringName, GroupData> &E : data.grouped) { if (E.value.group) { @@ -948,6 +942,18 @@ String Node::validate_child_name(Node *p_child) { } #endif +String Node::adjust_name_casing(const String &p_name) { + switch (GLOBAL_GET("editor/node_naming/name_casing").operator int()) { + case NAME_CASING_PASCAL_CASE: + return p_name.to_pascal_case(); + case NAME_CASING_CAMEL_CASE: + return p_name.to_camel_case(); + case NAME_CASING_SNAKE_CASE: + return p_name.to_snake_case(); + } + return p_name; +} + void Node::_validate_child_name(Node *p_child, bool p_force_human_readable) { /* Make sure the name is unique */ @@ -1018,22 +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. The current type name is expected to be in PascalCase. - switch (ProjectSettings::get_singleton()->get("editor/node_naming/name_casing").operator int()) { - case NAME_CASING_PASCAL_CASE: - break; - case NAME_CASING_CAMEL_CASE: { - String n = name; - n[0] = n.to_lower()[0]; - name = n; - } break; - case NAME_CASING_SNAKE_CASE: - name = String(name).camelcase_to_underscore(true); - break; - } } //quickly test if proposed name exists @@ -1111,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; @@ -1130,7 +1123,7 @@ void Node::_add_child_nocheck(Node *p_child, const StringName &p_name) { add_child_notify(p_child); } -void Node::add_child(Node *p_child, bool p_legible_unique_name, InternalMode p_internal) { +void Node::add_child(Node *p_child, bool p_force_readable_name, InternalMode p_internal) { ERR_FAIL_NULL(p_child); ERR_FAIL_COND_MSG(p_child == this, vformat("Can't add child '%s' to itself.", p_child->get_name())); // adding to itself! ERR_FAIL_COND_MSG(p_child->data.parent, vformat("Can't add child '%s' to '%s', already has a parent '%s'.", p_child->get_name(), get_name(), p_child->data.parent->get_name())); //Fail if node has a parent @@ -1139,7 +1132,7 @@ void Node::add_child(Node *p_child, bool p_legible_unique_name, InternalMode p_i #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."); - _validate_child_name(p_child, p_legible_unique_name); + _validate_child_name(p_child, p_force_readable_name); _add_child_nocheck(p_child, p_child->data.name); if (p_internal == INTERNAL_MODE_FRONT) { @@ -1153,7 +1146,7 @@ void Node::add_child(Node *p_child, bool p_legible_unique_name, InternalMode p_i } } -void Node::add_sibling(Node *p_sibling, bool p_legible_unique_name) { +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! @@ -1166,7 +1159,7 @@ void Node::add_sibling(Node *p_sibling, bool p_legible_unique_name) { internal = INTERNAL_MODE_BACK; } - data.parent->add_child(p_sibling, p_legible_unique_name, internal); + data.parent->add_child(p_sibling, p_force_readable_name, internal); data.parent->_move_child(p_sibling, get_index() + 1); } @@ -1178,9 +1171,9 @@ void Node::remove_child(Node *p_child) { 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; } } @@ -1216,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(); @@ -1348,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)); } } @@ -1416,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(); } } @@ -1471,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); @@ -1498,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; } @@ -1765,11 +1759,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); @@ -1778,8 +1772,8 @@ void Node::remove_from_group(const StringName &p_identifier) { data.grouped.remove(E); } -Array Node::_get_groups() const { - Array groups; +TypedArray<StringName> Node::_get_groups() const { + TypedArray<StringName> groups; List<GroupInfo> gi; get_groups(&gi); for (const GroupInfo &E : gi) { @@ -1909,9 +1903,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() { @@ -1921,43 +1915,6 @@ Ref<Tween> Node::create_tween() { return tween; } -void Node::remove_and_skip() { - ERR_FAIL_COND(!data.parent); - - Node *new_owner = get_owner(); - - List<Node *> children; - - while (true) { - bool clear = true; - for (int i = 0; i < data.children.size(); i++) { - Node *c_node = data.children[i]; - if (!c_node->get_owner()) { - continue; - } - - remove_child(c_node); - c_node->_propagate_replace_owner(this, nullptr); - children.push_back(c_node); - clear = false; - break; - } - - if (clear) { - break; - } - } - - while (!children.is_empty()) { - Node *c_node = children.front()->get(); - data.parent->add_child(c_node); - c_node->_propagate_replace_owner(nullptr, new_owner); - children.pop_front(); - } - - data.parent->remove_child(this); -} - void Node::set_scene_file_path(const String &p_scene_file_path) { data.scene_file_path = p_scene_file_path; } @@ -2173,9 +2130,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); } } @@ -2443,12 +2400,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()) { @@ -2623,11 +2580,15 @@ void Node::print_orphan_nodes() { #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); } } @@ -2682,29 +2643,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; } @@ -2786,11 +2745,11 @@ 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_method(D_METHOD("add_sibling", "sibling", "legible_unique_name"), &Node::add_sibling, DEFVAL(false)); + 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); ClassDB::bind_method(D_METHOD("get_name"), &Node::get_name); - ClassDB::bind_method(D_METHOD("add_child", "node", "legible_unique_name", "internal"), &Node::add_child, DEFVAL(false), DEFVAL(0)); + ClassDB::bind_method(D_METHOD("add_child", "node", "force_readable_name", "internal"), &Node::add_child, DEFVAL(false), DEFVAL(0)); ClassDB::bind_method(D_METHOD("remove_child", "node"), &Node::remove_child); ClassDB::bind_method(D_METHOD("get_child_count", "include_internal"), &Node::get_child_count, DEFVAL(false)); // Note that the default value bound for include_internal is false, while the method is declared with true. This is because internal nodes are irrelevant for GDSCript. ClassDB::bind_method(D_METHOD("get_children", "include_internal"), &Node::_get_children, DEFVAL(false)); @@ -2813,12 +2772,10 @@ void Node::_bind_methods() { 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("raise"), &Node::raise); ClassDB::bind_method(D_METHOD("set_owner", "owner"), &Node::set_owner); ClassDB::bind_method(D_METHOD("get_owner"), &Node::get_owner); - ClassDB::bind_method(D_METHOD("remove_and_skip"), &Node::remove_and_skip); ClassDB::bind_method(D_METHOD("get_index", "include_internal"), &Node::get_index, DEFVAL(false)); ClassDB::bind_method(D_METHOD("print_tree"), &Node::print_tree); ClassDB::bind_method(D_METHOD("print_tree_pretty"), &Node::print_tree_pretty); @@ -2869,7 +2826,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); @@ -2922,7 +2879,7 @@ void Node::_bind_methods() { BIND_CONSTANT(NOTIFICATION_PROCESS); BIND_CONSTANT(NOTIFICATION_PARENTED); BIND_CONSTANT(NOTIFICATION_UNPARENTED); - BIND_CONSTANT(NOTIFICATION_INSTANCED); + BIND_CONSTANT(NOTIFICATION_SCENE_INSTANTIATED); BIND_CONSTANT(NOTIFICATION_DRAG_BEGIN); BIND_CONSTANT(NOTIFICATION_DRAG_END); BIND_CONSTANT(NOTIFICATION_PATH_RENAMED); @@ -3005,7 +2962,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 ccd1d561d2..4a3ec253b1 100644 --- a/scene/main/node.h +++ b/scene/main/node.h @@ -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; @@ -181,13 +182,13 @@ private: Node *_duplicate(int p_flags, HashMap<const Node *, Node *> *r_duplimap = nullptr) const; TypedArray<Node> _get_children(bool p_include_internal = true) const; - Array _get_groups() const; + TypedArray<StringName> _get_groups() const; 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; @@ -259,7 +260,7 @@ public: NOTIFICATION_PROCESS = 17, NOTIFICATION_PARENTED = 18, NOTIFICATION_UNPARENTED = 19, - NOTIFICATION_INSTANCED = 20, + NOTIFICATION_SCENE_INSTANTIATED = 20, NOTIFICATION_DRAG_BEGIN = 21, NOTIFICATION_DRAG_END = 22, NOTIFICATION_PATH_RENAMED = 23, @@ -303,8 +304,8 @@ public: StringName get_name() const; void set_name(const String &p_name); - void add_child(Node *p_child, bool p_legible_unique_name = false, InternalMode p_internal = INTERNAL_MODE_DISABLED); - void add_sibling(Node *p_sibling, bool p_legible_unique_name = false); + void add_child(Node *p_child, bool p_force_readable_name = false, InternalMode p_internal = INTERNAL_MODE_DISABLED); + void add_sibling(Node *p_sibling, bool p_force_readable_name = false); void remove_child(Node *p_child); int get_child_count(bool p_include_internal = true) const; @@ -346,9 +347,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 raise(); + 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; @@ -357,7 +357,6 @@ public: void set_unique_name_in_owner(bool p_enabled); bool is_unique_name_in_owner() const; - void remove_and_skip(); int get_index(bool p_include_internal = true) const; Ref<Tween> create_tween(); @@ -459,8 +458,9 @@ public: #ifdef TOOLS_ENABLED String validate_child_name(Node *p_child); #endif + static String adjust_name_casing(const String &p_name); - void queue_delete(); + void queue_free(); //hacks for speed static void init_node_hrcr(); @@ -478,7 +478,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(); @@ -530,4 +530,8 @@ Error Node::rpc_id(int p_peer_id, const StringName &p_method, VarArgs... p_args) return rpcp(p_peer_id, p_method, sizeof...(p_args) == 0 ? nullptr : (const Variant **)argptrs, sizeof...(p_args)); } +// Add these macro to your class's 'get_configuration_warnings' function to have warnings show up in the scene tree inspector. +#define DEPRECATED_NODE_WARNING warnings.push_back(RTR("This node is marked as deprecated and will be removed in future versions.\nPlease check the Godot documentation for information about migration.")); +#define EXPERIMENTAL_NODE_WARNING warnings.push_back(RTR("This node is marked as experimental and may be subject to removal or major changes in future versions.")); + #endif // NODE_H diff --git a/scene/main/scene_tree.cpp b/scene/main/scene_tree.cpp index 644fb3e9cc..81a4e3073b 100644 --- a/scene/main/scene_tree.cpp +++ b/scene/main/scene_tree.cpp @@ -44,8 +44,10 @@ #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" #include "scene/resources/font.h" #include "scene/resources/material.h" #include "scene/resources/mesh.h" @@ -87,6 +89,14 @@ bool SceneTreeTimer::is_process_always() { return process_always; } +void SceneTreeTimer::set_process_in_physics(bool p_process_in_physics) { + process_in_physics = p_process_in_physics; +} + +bool SceneTreeTimer::is_process_in_physics() { + return process_in_physics; +} + void SceneTreeTimer::set_ignore_time_scale(bool p_ignore) { ignore_time_scale = p_ignore; } @@ -96,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); } } @@ -113,6 +123,9 @@ void SceneTree::tree_changed() { void SceneTree::node_added(Node *p_node) { emit_signal(node_added_name, p_node); + if (call_lock > 0) { + call_skip.erase(p_node->get_instance_id()); + } } void SceneTree::node_removed(Node *p_node) { @@ -121,7 +134,7 @@ void SceneTree::node_removed(Node *p_node) { } emit_signal(node_removed_name, p_node); if (call_lock > 0) { - call_skip.insert(p_node); + call_skip.insert(p_node->get_instance_id()); } } @@ -198,15 +211,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; } @@ -244,36 +257,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]->get_instance_id())) { 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]->get_instance_id())) { 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); } } } @@ -297,34 +310,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]->get_instance_id())) { 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]->get_instance_id())) { 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); } } } @@ -348,34 +361,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]->get_instance_id())) { 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]->get_instance_id())) { 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); } } } @@ -419,6 +432,8 @@ bool SceneTree::physics_process(double p_time) { _flush_ugc(); MessageQueue::get_singleton()->flush(); //small little hack + process_timers(p_time, true); //go through timers + process_tweens(p_time, true); flush_transform_notifications(); @@ -461,37 +476,7 @@ bool SceneTree::process(double p_time) { _flush_delete_queue(); - //go through timers - - List<Ref<SceneTreeTimer>>::Element *L = timers.back(); //last element - - for (List<Ref<SceneTreeTimer>>::Element *E = timers.front(); E;) { - List<Ref<SceneTreeTimer>>::Element *N = E->next(); - if (paused && !E->get()->is_process_always()) { - if (E == L) { - break; //break on last, so if new timers were added during list traversal, ignore them. - } - E = N; - continue; - } - - double time_left = E->get()->get_time_left(); - if (E->get()->is_ignore_time_scale()) { - time_left -= Engine::get_singleton()->get_process_step(); - } else { - time_left -= p_time; - } - E->get()->set_time_left(time_left); - - if (time_left <= 0) { - E->get()->emit_signal(SNAME("timeout")); - timers.erase(E); - } - if (E == L) { - break; //break on last, so if new timers were added during list traversal, ignore them. - } - E = N; - } + process_timers(p_time, false); //go through timers process_tweens(p_time, false); @@ -503,7 +488,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(); @@ -529,7 +514,39 @@ bool SceneTree::process(double p_time) { return _quit; } -void SceneTree::process_tweens(float p_delta, bool p_physics) { +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;) { + List<Ref<SceneTreeTimer>>::Element *N = E->next(); + if ((paused && !E->get()->is_process_always()) || (E->get()->is_process_in_physics() != p_physics_frame)) { + if (E == L) { + break; //break on last, so if new timers were added during list traversal, ignore them. + } + E = N; + continue; + } + + double time_left = E->get()->get_time_left(); + if (E->get()->is_ignore_time_scale()) { + time_left -= Engine::get_singleton()->get_process_step(); + } else { + time_left -= p_delta; + } + E->get()->set_time_left(time_left); + + if (time_left <= 0) { + E->get()->emit_signal(SNAME("timeout")); + timers.erase(E); + } + if (E == L) { + break; //break on last, so if new timers were added during list traversal, ignore them. + } + E = N; + } +} + +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(); @@ -709,22 +726,6 @@ float SceneTree::get_debug_paths_width() const { return debug_paths_width; } -void SceneTree::set_debug_navigation_color(const Color &p_color) { - debug_navigation_color = p_color; -} - -Color SceneTree::get_debug_navigation_color() const { - return debug_navigation_color; -} - -void SceneTree::set_debug_navigation_disabled_color(const Color &p_color) { - debug_navigation_disabled_color = p_color; -} - -Color SceneTree::get_debug_navigation_disabled_color() const { - return debug_navigation_disabled_color; -} - Ref<Material> SceneTree::get_debug_paths_material() { if (debug_paths_material.is_valid()) { return debug_paths_material; @@ -742,40 +743,6 @@ Ref<Material> SceneTree::get_debug_paths_material() { return debug_paths_material; } -Ref<Material> SceneTree::get_debug_navigation_material() { - if (navigation_material.is_valid()) { - return navigation_material; - } - - Ref<StandardMaterial3D> line_material = Ref<StandardMaterial3D>(memnew(StandardMaterial3D)); - line_material->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED); - line_material->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA); - line_material->set_flag(StandardMaterial3D::FLAG_SRGB_VERTEX_COLOR, true); - line_material->set_flag(StandardMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true); - line_material->set_albedo(get_debug_navigation_color()); - - navigation_material = line_material; - - return navigation_material; -} - -Ref<Material> SceneTree::get_debug_navigation_disabled_material() { - if (navigation_disabled_material.is_valid()) { - return navigation_disabled_material; - } - - Ref<StandardMaterial3D> line_material = Ref<StandardMaterial3D>(memnew(StandardMaterial3D)); - line_material->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED); - line_material->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA); - line_material->set_flag(StandardMaterial3D::FLAG_SRGB_VERTEX_COLOR, true); - line_material->set_flag(StandardMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true); - line_material->set_albedo(get_debug_navigation_disabled_color()); - - navigation_disabled_material = line_material; - - return navigation_disabled_material; -} - Ref<Material> SceneTree::get_debug_collision_material() { if (collision_material.is_valid()) { return collision_material; @@ -883,14 +850,14 @@ 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]; - if (call_lock && call_skip.has(n)) { + for (int i = 0; i < gr_node_count; i++) { + Node *n = gr_nodes[i]; + if (call_lock && call_skip.has(n->get_instance_id())) { continue; } @@ -902,7 +869,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--; @@ -927,18 +894,20 @@ 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]; - if (call_lock && call_skip.has(n)) { + Node *n = gr_nodes[i]; + if (call_lock && call_skip.has(n->get_instance_id())) { continue; } @@ -950,9 +919,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; @@ -962,6 +944,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(); @@ -1000,8 +989,8 @@ int64_t SceneTree::get_frame() const { return current_frame; } -Array SceneTree::_get_nodes_in_group(const StringName &p_group) { - Array ret; +TypedArray<Node> SceneTree::_get_nodes_in_group(const StringName &p_group) { + TypedArray<Node> ret; HashMap<StringName, Group>::Iterator E = group_map.find(p_group); if (!E) { return ret; @@ -1122,19 +1111,20 @@ void SceneTree::_change_scene(Node *p_to) { if (p_to) { current_scene = p_to; root->add_child(p_to); + root->update_mouse_cursor_shape(); } } -Error SceneTree::change_scene(const String &p_path) { +Error SceneTree::change_scene_to_file(const String &p_path) { Ref<PackedScene> new_scene = ResourceLoader::load(p_path); if (new_scene.is_null()) { return ERR_CANT_OPEN; } - return change_scene_to(new_scene); + return change_scene_to_packed(new_scene); } -Error SceneTree::change_scene_to(const Ref<PackedScene> &p_scene) { +Error SceneTree::change_scene_to_packed(const Ref<PackedScene> &p_scene) { Node *new_scene = nullptr; if (p_scene.is_valid()) { new_scene = p_scene->instantiate(); @@ -1148,7 +1138,7 @@ Error SceneTree::change_scene_to(const Ref<PackedScene> &p_scene) { Error SceneTree::reload_current_scene() { ERR_FAIL_COND_V(!current_scene, ERR_UNCONFIGURED); String fname = current_scene->get_scene_file_path(); - return change_scene(fname); + return change_scene_to_file(fname); } void SceneTree::add_current_scene(Node *p_current) { @@ -1156,11 +1146,13 @@ void SceneTree::add_current_scene(Node *p_current) { root->add_child(p_current); } -Ref<SceneTreeTimer> SceneTree::create_timer(double p_delay_sec, bool p_process_always) { +Ref<SceneTreeTimer> SceneTree::create_timer(double p_delay_sec, bool p_process_always, bool p_process_in_physics, bool p_ignore_time_scale) { Ref<SceneTreeTimer> stt; stt.instantiate(); stt->set_process_always(p_process_always); stt->set_time_left(p_delay_sec); + stt->set_process_in_physics(p_process_in_physics); + stt->set_ignore_time_scale(p_ignore_time_scale); timers.push_back(stt); return stt; } @@ -1171,8 +1163,8 @@ Ref<Tween> SceneTree::create_tween() { return tween; } -Array SceneTree::get_processed_tweens() { - Array ret; +TypedArray<Tween> SceneTree::get_processed_tweens() { + TypedArray<Tween> ret; ret.resize(tweens.size()); int i = 0; @@ -1258,7 +1250,7 @@ void SceneTree::_bind_methods() { ClassDB::bind_method(D_METHOD("set_pause", "enable"), &SceneTree::set_pause); ClassDB::bind_method(D_METHOD("is_paused"), &SceneTree::is_paused); - ClassDB::bind_method(D_METHOD("create_timer", "time_sec", "process_always"), &SceneTree::create_timer, DEFVAL(true)); + ClassDB::bind_method(D_METHOD("create_timer", "time_sec", "process_always", "process_in_physics", "ignore_time_scale"), &SceneTree::create_timer, DEFVAL(true), DEFVAL(false), DEFVAL(false)); ClassDB::bind_method(D_METHOD("create_tween"), &SceneTree::create_tween); ClassDB::bind_method(D_METHOD("get_processed_tweens"), &SceneTree::get_processed_tweens); @@ -1295,8 +1287,8 @@ void SceneTree::_bind_methods() { ClassDB::bind_method(D_METHOD("set_current_scene", "child_node"), &SceneTree::set_current_scene); ClassDB::bind_method(D_METHOD("get_current_scene"), &SceneTree::get_current_scene); - ClassDB::bind_method(D_METHOD("change_scene", "path"), &SceneTree::change_scene); - ClassDB::bind_method(D_METHOD("change_scene_to", "packed_scene"), &SceneTree::change_scene_to); + ClassDB::bind_method(D_METHOD("change_scene_to_file", "path"), &SceneTree::change_scene_to_file); + ClassDB::bind_method(D_METHOD("change_scene_to_packed", "packed_scene"), &SceneTree::change_scene_to_packed); ClassDB::bind_method(D_METHOD("reload_current_scene"), &SceneTree::reload_current_scene); @@ -1351,7 +1343,7 @@ void SceneTree::add_idle_callback(IdleCallback p_callback) { } void SceneTree::get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const { - if (p_function == "change_scene") { + if (p_function == "change_scene_to_file") { Ref<DirAccess> dir_access = DirAccess::create(DirAccess::ACCESS_RESOURCES); List<String> directories; directories.push_back(dir_access->get_current_dir()); @@ -1370,9 +1362,9 @@ void SceneTree::get_argument_options(const StringName &p_function, int p_idx, Li } if (dir_access->dir_exists(filename)) { - directories.push_back(dir_access->get_current_dir().plus_file(filename)); + directories.push_back(dir_access->get_current_dir().path_join(filename)); } else if (filename.ends_with(".tscn") || filename.ends_with(".scn")) { - r_options->push_back("\"" + dir_access->get_current_dir().plus_file(filename) + "\""); + r_options->push_back("\"" + dir_access->get_current_dir().path_join(filename) + "\""); } filename = dir_access->get_next(); @@ -1389,8 +1381,6 @@ SceneTree::SceneTree() { debug_collision_contact_color = GLOBAL_DEF("debug/shapes/collision/contact_color", Color(1.0, 0.2, 0.1, 0.8)); debug_paths_color = GLOBAL_DEF("debug/shapes/paths/geometry_color", Color(0.1, 1.0, 0.7, 0.4)); debug_paths_width = GLOBAL_DEF("debug/shapes/paths/geometry_width", 2.0); - debug_navigation_color = GLOBAL_DEF("debug/shapes/navigation/geometry_color", Color(0.1, 1.0, 0.7, 0.4)); - debug_navigation_disabled_color = GLOBAL_DEF("debug/shapes/navigation/disabled_geometry_color", Color(1.0, 0.7, 0.1, 0.4)); collision_debug_contacts = GLOBAL_DEF("debug/shapes/collision/max_contacts_displayed", 10000); ProjectSettings::get_singleton()->set_custom_property_info("debug/shapes/collision/max_contacts_displayed", PropertyInfo(Variant::INT, "debug/shapes/collision/max_contacts_displayed", PROPERTY_HINT_RANGE, "0,20000,1")); // No negative @@ -1403,7 +1393,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()) { @@ -1418,9 +1408,16 @@ SceneTree::SceneTree() { root->set_as_audio_listener_2d(true); current_scene = nullptr; - const int msaa_mode = GLOBAL_DEF_BASIC("rendering/anti_aliasing/quality/msaa", 0); - ProjectSettings::get_singleton()->set_custom_property_info("rendering/anti_aliasing/quality/msaa", PropertyInfo(Variant::INT, "rendering/anti_aliasing/quality/msaa", PROPERTY_HINT_ENUM, String::utf8("Disabled (Fastest),2× (Average),4× (Slow),8× (Slowest)"))); - root->set_msaa(Viewport::MSAA(msaa_mode)); + const int msaa_mode_2d = GLOBAL_DEF_BASIC("rendering/anti_aliasing/quality/msaa_2d", 0); + ProjectSettings::get_singleton()->set_custom_property_info("rendering/anti_aliasing/quality/msaa_2d", PropertyInfo(Variant::INT, "rendering/anti_aliasing/quality/msaa_2d", PROPERTY_HINT_ENUM, String::utf8("Disabled (Fastest),2× (Average),4× (Slow),8× (Slowest)"))); + root->set_msaa_2d(Viewport::MSAA(msaa_mode_2d)); + + const int msaa_mode_3d = GLOBAL_DEF_BASIC("rendering/anti_aliasing/quality/msaa_3d", 0); + 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)")); @@ -1468,18 +1465,18 @@ SceneTree::SceneTree() { } } - int shadowmap_size = GLOBAL_DEF("rendering/shadows/positional_shadow/atlas_size", 4096); - ProjectSettings::get_singleton()->set_custom_property_info("rendering/shadows/positional_shadow/atlas_size", PropertyInfo(Variant::INT, "rendering/shadows/positional_shadow/atlas_size", PROPERTY_HINT_RANGE, "256,16384")); - GLOBAL_DEF("rendering/shadows/positional_shadow/atlas_size.mobile", 2048); - bool shadowmap_16_bits = GLOBAL_DEF("rendering/shadows/positional_shadow/atlas_16_bits", true); - int atlas_q0 = GLOBAL_DEF("rendering/shadows/positional_shadow/atlas_quadrant_0_subdiv", 2); - int atlas_q1 = GLOBAL_DEF("rendering/shadows/positional_shadow/atlas_quadrant_1_subdiv", 2); - int atlas_q2 = GLOBAL_DEF("rendering/shadows/positional_shadow/atlas_quadrant_2_subdiv", 3); - int atlas_q3 = GLOBAL_DEF("rendering/shadows/positional_shadow/atlas_quadrant_3_subdiv", 4); - ProjectSettings::get_singleton()->set_custom_property_info("rendering/shadows/positional_shadow/atlas_quadrant_0_subdiv", PropertyInfo(Variant::INT, "rendering/shadows/positional_shadow/atlas_quadrant_0_subdiv", PROPERTY_HINT_ENUM, "Disabled,1 Shadow,4 Shadows,16 Shadows,64 Shadows,256 Shadows,1024 Shadows")); - ProjectSettings::get_singleton()->set_custom_property_info("rendering/shadows/positional_shadow/atlas_quadrant_1_subdiv", PropertyInfo(Variant::INT, "rendering/shadows/positional_shadow/atlas_quadrant_1_subdiv", PROPERTY_HINT_ENUM, "Disabled,1 Shadow,4 Shadows,16 Shadows,64 Shadows,256 Shadows,1024 Shadows")); - ProjectSettings::get_singleton()->set_custom_property_info("rendering/shadows/positional_shadow/atlas_quadrant_2_subdiv", PropertyInfo(Variant::INT, "rendering/shadows/positional_shadow/atlas_quadrant_2_subdiv", PROPERTY_HINT_ENUM, "Disabled,1 Shadow,4 Shadows,16 Shadows,64 Shadows,256 Shadows,1024 Shadows")); - ProjectSettings::get_singleton()->set_custom_property_info("rendering/shadows/positional_shadow/atlas_quadrant_3_subdiv", PropertyInfo(Variant::INT, "rendering/shadows/positional_shadow/atlas_quadrant_3_subdiv", PROPERTY_HINT_ENUM, "Disabled,1 Shadow,4 Shadows,16 Shadows,64 Shadows,256 Shadows,1024 Shadows")); + int shadowmap_size = GLOBAL_DEF("rendering/lights_and_shadows/positional_shadow/atlas_size", 4096); + ProjectSettings::get_singleton()->set_custom_property_info("rendering/lights_and_shadows/positional_shadow/atlas_size", PropertyInfo(Variant::INT, "rendering/lights_and_shadows/positional_shadow/atlas_size", PROPERTY_HINT_RANGE, "256,16384")); + GLOBAL_DEF("rendering/lights_and_shadows/positional_shadow/atlas_size.mobile", 2048); + bool shadowmap_16_bits = GLOBAL_DEF("rendering/lights_and_shadows/positional_shadow/atlas_16_bits", true); + int atlas_q0 = GLOBAL_DEF("rendering/lights_and_shadows/positional_shadow/atlas_quadrant_0_subdiv", 2); + int atlas_q1 = GLOBAL_DEF("rendering/lights_and_shadows/positional_shadow/atlas_quadrant_1_subdiv", 2); + int atlas_q2 = GLOBAL_DEF("rendering/lights_and_shadows/positional_shadow/atlas_quadrant_2_subdiv", 3); + int atlas_q3 = GLOBAL_DEF("rendering/lights_and_shadows/positional_shadow/atlas_quadrant_3_subdiv", 4); + ProjectSettings::get_singleton()->set_custom_property_info("rendering/lights_and_shadows/positional_shadow/atlas_quadrant_0_subdiv", PropertyInfo(Variant::INT, "rendering/lights_and_shadows/positional_shadow/atlas_quadrant_0_subdiv", PROPERTY_HINT_ENUM, "Disabled,1 Shadow,4 Shadows,16 Shadows,64 Shadows,256 Shadows,1024 Shadows")); + ProjectSettings::get_singleton()->set_custom_property_info("rendering/lights_and_shadows/positional_shadow/atlas_quadrant_1_subdiv", PropertyInfo(Variant::INT, "rendering/lights_and_shadows/positional_shadow/atlas_quadrant_1_subdiv", PROPERTY_HINT_ENUM, "Disabled,1 Shadow,4 Shadows,16 Shadows,64 Shadows,256 Shadows,1024 Shadows")); + ProjectSettings::get_singleton()->set_custom_property_info("rendering/lights_and_shadows/positional_shadow/atlas_quadrant_2_subdiv", PropertyInfo(Variant::INT, "rendering/lights_and_shadows/positional_shadow/atlas_quadrant_2_subdiv", PROPERTY_HINT_ENUM, "Disabled,1 Shadow,4 Shadows,16 Shadows,64 Shadows,256 Shadows,1024 Shadows")); + ProjectSettings::get_singleton()->set_custom_property_info("rendering/lights_and_shadows/positional_shadow/atlas_quadrant_3_subdiv", PropertyInfo(Variant::INT, "rendering/lights_and_shadows/positional_shadow/atlas_quadrant_3_subdiv", PROPERTY_HINT_ENUM, "Disabled,1 Shadow,4 Shadows,16 Shadows,64 Shadows,256 Shadows,1024 Shadows")); root->set_positional_shadow_atlas_size(shadowmap_size); root->set_positional_shadow_atlas_16_bits(shadowmap_16_bits); diff --git a/scene/main/scene_tree.h b/scene/main/scene_tree.h index a512feacc8..d4fcb288ae 100644 --- a/scene/main/scene_tree.h +++ b/scene/main/scene_tree.h @@ -53,6 +53,7 @@ class SceneTreeTimer : public RefCounted { double time_left = 0.0; bool process_always = true; + bool process_in_physics = false; bool ignore_time_scale = false; protected: @@ -65,6 +66,9 @@ public: void set_process_always(bool p_process_always); bool is_process_always(); + void set_process_in_physics(bool p_process_in_physics); + bool is_process_in_physics(); + void set_ignore_time_scale(bool p_ignore); bool is_ignore_time_scale(); @@ -131,7 +135,7 @@ private: // Safety for when a node is deleted while a group is being called. int call_lock = 0; - HashSet<Node *> call_skip; // Skip erased nodes. + HashSet<ObjectID> call_skip; // Skip erased nodes. Store ID instead of pointer to avoid false positives when node is freed and a new node is allocated at the pointed address. List<ObjectID> delete_queue; @@ -141,7 +145,7 @@ private: _FORCE_INLINE_ void _update_group_order(Group &g, bool p_use_priority = false); - Array _get_nodes_in_group(const StringName &p_group); + TypedArray<Node> _get_nodes_in_group(const StringName &p_group); Node *current_scene = nullptr; @@ -176,7 +180,8 @@ private: void node_added(Node *p_node); void node_removed(Node *p_node); void node_renamed(Node *p_node); - 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); @@ -329,15 +334,7 @@ public: void set_debug_paths_width(float p_width); float get_debug_paths_width() const; - void set_debug_navigation_color(const Color &p_color); - Color get_debug_navigation_color() const; - - void set_debug_navigation_disabled_color(const Color &p_color); - Color get_debug_navigation_disabled_color() const; - Ref<Material> get_debug_paths_material(); - Ref<Material> get_debug_navigation_material(); - Ref<Material> get_debug_navigation_disabled_material(); Ref<Material> get_debug_collision_material(); Ref<ArrayMesh> get_debug_contact_mesh(); @@ -361,13 +358,13 @@ public: void set_current_scene(Node *p_scene); Node *get_current_scene() const; - Error change_scene(const String &p_path); - Error change_scene_to(const Ref<PackedScene> &p_scene); + Error change_scene_to_file(const String &p_path); + Error change_scene_to_packed(const Ref<PackedScene> &p_scene); Error reload_current_scene(); - Ref<SceneTreeTimer> create_timer(double p_delay_sec, bool p_process_always = true); + Ref<SceneTreeTimer> create_timer(double p_delay_sec, bool p_process_always = true, bool p_process_in_physics = false, bool p_ignore_time_scale = false); Ref<Tween> create_tween(); - Array get_processed_tweens(); + TypedArray<Tween> get_processed_tweens(); //used by Main::start, don't use otherwise void add_current_scene(Node *p_current); diff --git a/scene/main/shader_globals_override.cpp b/scene/main/shader_globals_override.cpp index a621aea9c8..455b8c6866 100644 --- a/scene/main/shader_globals_override.cpp +++ b/scene/main/shader_globals_override.cpp @@ -64,9 +64,9 @@ bool ShaderGlobalsOverride::_set(const StringName &p_name, const Variant &p_valu if (active) { if (o->override.get_type() == Variant::OBJECT) { RID tex_rid = p_value; - RS::get_singleton()->global_shader_uniform_set_override(*r, tex_rid); + RS::get_singleton()->global_shader_parameter_set_override(*r, tex_rid); } else { - RS::get_singleton()->global_shader_uniform_set_override(*r, p_value); + RS::get_singleton()->global_shader_parameter_set_override(*r, p_value); } } o->in_use = p_value.get_type() != Variant::NIL; @@ -93,13 +93,13 @@ bool ShaderGlobalsOverride::_get(const StringName &p_name, Variant &r_ret) const void ShaderGlobalsOverride::_get_property_list(List<PropertyInfo> *p_list) const { Vector<StringName> variables; - variables = RS::get_singleton()->global_shader_uniform_get_list(); + variables = RS::get_singleton()->global_shader_parameter_get_list(); for (int i = 0; i < variables.size(); i++) { PropertyInfo pinfo; pinfo.name = "params/" + variables[i]; pinfo.usage = PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_CHECKABLE; - switch (RS::get_singleton()->global_shader_uniform_get_type(variables[i])) { + switch (RS::get_singleton()->global_shader_parameter_get_type(variables[i])) { case RS::GLOBAL_VAR_TYPE_BOOL: { pinfo.type = Variant::BOOL; } break; @@ -234,9 +234,9 @@ void ShaderGlobalsOverride::_activate() { if (o->in_use && o->override.get_type() != Variant::NIL) { if (o->override.get_type() == Variant::OBJECT) { RID tex_rid = o->override; - RS::get_singleton()->global_shader_uniform_set_override(E.key, tex_rid); + RS::get_singleton()->global_shader_parameter_set_override(E.key, tex_rid); } else { - RS::get_singleton()->global_shader_uniform_set_override(E.key, o->override); + RS::get_singleton()->global_shader_parameter_set_override(E.key, o->override); } } @@ -258,7 +258,7 @@ void ShaderGlobalsOverride::_notification(int p_what) { for (const KeyValue<StringName, Override> &E : overrides) { const Override *o = &E.value; if (o->in_use) { - RS::get_singleton()->global_shader_uniform_set_override(E.key, Variant()); + RS::get_singleton()->global_shader_parameter_set_override(E.key, Variant()); } } } @@ -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 584fad9648..4c2a761138 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); @@ -190,14 +190,7 @@ void Viewport::_sub_window_register(Window *p_window) { } void Viewport::_sub_window_update(Window *p_window) { - int index = -1; - for (int i = 0; i < gui.sub_windows.size(); i++) { - if (gui.sub_windows[i].window == p_window) { - index = i; - break; - } - } - + int index = _sub_window_find(p_window); ERR_FAIL_COND(index == -1); const SubWindow &sw = gui.sub_windows[index]; @@ -257,14 +250,7 @@ void Viewport::_sub_window_grab_focus(Window *p_window) { return; } - int index = -1; - for (int i = 0; i < gui.sub_windows.size(); i++) { - if (gui.sub_windows[i].window == p_window) { - index = i; - break; - } - } - + int index = _sub_window_find(p_window); ERR_FAIL_COND(index == -1); if (p_window->get_flag(Window::FLAG_NO_FOCUS)) { @@ -312,13 +298,11 @@ void Viewport::_sub_window_grab_focus(Window *p_window) { } void Viewport::_sub_window_remove(Window *p_window) { - for (int i = 0; i < gui.sub_windows.size(); i++) { - if (gui.sub_windows[i].window == p_window) { - RS::get_singleton()->free(gui.sub_windows[i].canvas_item); - gui.sub_windows.remove_at(i); - break; - } - } + int index = _sub_window_find(p_window); + ERR_FAIL_COND(index == -1); + + RS::get_singleton()->free(gui.sub_windows[index].canvas_item); + gui.sub_windows.remove_at(index); if (gui.sub_windows.size() == 0) { RS::get_singleton()->free(subwindow_canvas); @@ -326,27 +310,46 @@ void Viewport::_sub_window_remove(Window *p_window) { } if (gui.subwindow_focused == p_window) { + Window *new_focused_window; Window *parent_visible = p_window->get_parent_visible_window(); gui.subwindow_drag = SUB_WINDOW_DRAG_DISABLED; gui.subwindow_focused->_event_callback(DisplayServer::WINDOW_EVENT_FOCUS_OUT); - if (parent_visible && parent_visible != this) { - gui.subwindow_focused = parent_visible; - gui.subwindow_focused->_event_callback(DisplayServer::WINDOW_EVENT_FOCUS_IN); + if (parent_visible) { + new_focused_window = parent_visible; } else { - gui.subwindow_focused = nullptr; - Window *this_window = Object::cast_to<Window>(this); - if (this_window) { - this_window->_event_callback(DisplayServer::WINDOW_EVENT_FOCUS_IN); + new_focused_window = Object::cast_to<Window>(this); + } + + if (new_focused_window) { + int new_focused_index = _sub_window_find(new_focused_window); + if (new_focused_index != -1) { + gui.subwindow_focused = new_focused_window; + } else { + gui.subwindow_focused = nullptr; } + + new_focused_window->_event_callback(DisplayServer::WINDOW_EVENT_FOCUS_IN); + } else { + gui.subwindow_focused = nullptr; } } RenderingServer::get_singleton()->viewport_set_parent_viewport(p_window->viewport, p_window->parent ? p_window->parent->viewport : RID()); } +int Viewport::_sub_window_find(Window *p_window) { + for (int i = 0; i < gui.sub_windows.size(); i++) { + if (gui.sub_windows[i].window == p_window) { + return i; + } + } + + return -1; +} + void Viewport::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: { @@ -359,6 +362,7 @@ 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_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()); @@ -496,6 +500,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; } } @@ -625,19 +636,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; @@ -792,19 +803,46 @@ 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(); update_canvas_items(); + for (ViewportTexture *E : viewport_textures) { + E->emit_changed(); + } + emit_signal(SNAME("size_changed")); } 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; } @@ -956,11 +994,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); } @@ -1022,7 +1055,7 @@ void Viewport::_update_canvas_items(Node *p_node) { CanvasItem *ci = Object::cast_to<CanvasItem>(p_node); if (ci) { - ci->update(); + ci->queue_redraw(); } } @@ -1103,7 +1136,7 @@ Vector2 Viewport::get_mouse_position() const { void Viewport::warp_mouse(const Vector2 &p_position) { Transform2D xform = get_screen_transform(); - Vector2 gpos = xform.xform(p_position).round(); + Vector2 gpos = xform.xform(p_position); Input::get_singleton()->warp_mouse(gpos); } @@ -1124,9 +1157,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(); } } @@ -1189,8 +1220,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) { @@ -1222,16 +1251,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. @@ -1369,10 +1405,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. } @@ -1456,7 +1488,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) { @@ -1530,44 +1561,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. @@ -1610,8 +1612,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(); @@ -1653,7 +1653,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); } } @@ -1669,6 +1669,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; } @@ -1708,15 +1711,7 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) { is_tooltip_shown = true; } } else { - Variant t = gui.tooltip_popup->call("get_tooltip_text"); - - if (t.get_type() == Variant::STRING) { - if (tooltip == String(t)) { - is_tooltip_shown = true; - } - } else { - is_tooltip_shown = true; // Nothing to compare against, likely using custom control, so if it changes there is nothing we can do. - } + is_tooltip_shown = true; // Nothing to compare against, likely using custom control, so if it changes there is nothing we can do. } } else { _gui_cancel_tooltip(); @@ -1774,7 +1769,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(); @@ -1875,7 +1870,9 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) { } } - DisplayServer::get_singleton()->cursor_set_shape(ds_cursor_shape); + if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_CURSOR_SHAPE)) { + DisplayServer::get_singleton()->cursor_set_shape(ds_cursor_shape); + } } Ref<InputEventScreenTouch> touch_event = p_event; @@ -1974,6 +1971,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(); } @@ -2055,6 +2058,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()); @@ -2101,7 +2125,7 @@ void Viewport::_gui_set_drag_preview(Control *p_base, Control *p_control) { p_control->set_as_top_level(true); p_control->set_position(gui.last_mouse_pos); p_base->get_root_parent_control()->add_child(p_control); // Add as child of viewport. - p_control->raise(); + p_control->move_to_front(); gui.drag_preview_id = p_control->get_instance_id(); } @@ -2202,7 +2226,7 @@ void Viewport::_gui_control_grab_focus(Control *p_control) { gui.key_focus = p_control; emit_signal(SNAME("gui_focus_changed"), p_control); p_control->notification(Control::NOTIFICATION_FOCUS_ENTER); - p_control->update(); + p_control->queue_redraw(); } void Viewport::_gui_accept_event() { @@ -2639,6 +2663,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; @@ -2684,7 +2713,9 @@ bool Viewport::_sub_windows_forward_input(const Ref<InputEvent> &p_event) { DisplayServer::CURSOR_FDIAGSIZE }; - DisplayServer::get_singleton()->cursor_set_shape(shapes[resize]); + if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_CURSOR_SHAPE)) { + DisplayServer::get_singleton()->cursor_set_shape(shapes[resize]); + } return true; // Reserved for showing the resize cursor. } @@ -2729,6 +2760,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; @@ -2773,7 +2809,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); } @@ -2846,11 +2882,11 @@ 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 == 0 || size.y == 0) { - warnings.push_back(RTR("Viewport size must be greater than 0 to render anything.")); + 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.")); } return warnings; } @@ -2868,7 +2904,7 @@ void Viewport::gui_release_focus() { Control *f = gui.key_focus; gui.key_focus = nullptr; f->notification(Control::NOTIFICATION_FOCUS_EXIT, true); - f->update(); + f->queue_redraw(); } } @@ -2876,17 +2912,30 @@ Control *Viewport::gui_get_focus_owner() { return gui.key_focus; } -void Viewport::set_msaa(MSAA p_msaa) { +void Viewport::set_msaa_2d(MSAA p_msaa) { + ERR_FAIL_INDEX(p_msaa, MSAA_MAX); + if (msaa_2d == p_msaa) { + return; + } + msaa_2d = p_msaa; + RS::get_singleton()->viewport_set_msaa_2d(viewport, RS::ViewportMSAA(p_msaa)); +} + +Viewport::MSAA Viewport::get_msaa_2d() const { + return msaa_2d; +} + +void Viewport::set_msaa_3d(MSAA p_msaa) { ERR_FAIL_INDEX(p_msaa, MSAA_MAX); - if (msaa == p_msaa) { + if (msaa_3d == p_msaa) { return; } - msaa = p_msaa; - RS::get_singleton()->viewport_set_msaa(viewport, RS::ViewportMSAA(p_msaa)); + msaa_3d = p_msaa; + RS::get_singleton()->viewport_set_msaa_3d(viewport, RS::ViewportMSAA(p_msaa)); } -Viewport::MSAA Viewport::get_msaa() const { - return msaa; +Viewport::MSAA Viewport::get_msaa_3d() const { + return msaa_3d; } void Viewport::set_screen_space_aa(ScreenSpaceAA p_screen_space_aa) { @@ -2998,8 +3047,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; @@ -3202,6 +3249,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; @@ -3595,9 +3665,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() { @@ -3673,13 +3754,17 @@ 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); ClassDB::bind_method(D_METHOD("has_transparent_background"), &Viewport::has_transparent_background); - ClassDB::bind_method(D_METHOD("set_msaa", "msaa"), &Viewport::set_msaa); - ClassDB::bind_method(D_METHOD("get_msaa"), &Viewport::get_msaa); + ClassDB::bind_method(D_METHOD("set_msaa_2d", "msaa"), &Viewport::set_msaa_2d); + ClassDB::bind_method(D_METHOD("get_msaa_2d"), &Viewport::get_msaa_2d); + + ClassDB::bind_method(D_METHOD("set_msaa_3d", "msaa"), &Viewport::set_msaa_3d); + ClassDB::bind_method(D_METHOD("get_msaa_3d"), &Viewport::get_msaa_3d); ClassDB::bind_method(D_METHOD("set_screen_space_aa", "screen_space_aa"), &Viewport::set_screen_space_aa); ClassDB::bind_method(D_METHOD("get_screen_space_aa"), &Viewport::get_screen_space_aa); @@ -3758,6 +3843,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); @@ -3819,7 +3910,8 @@ void Viewport::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "snap_2d_transforms_to_pixel"), "set_snap_2d_transforms_to_pixel", "is_snap_2d_transforms_to_pixel_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "snap_2d_vertices_to_pixel"), "set_snap_2d_vertices_to_pixel", "is_snap_2d_vertices_to_pixel_enabled"); ADD_GROUP("Rendering", ""); - ADD_PROPERTY(PropertyInfo(Variant::INT, "msaa", PROPERTY_HINT_ENUM, String::utf8("Disabled (Fastest),2× (Average),4× (Slow),8× (Slowest)")), "set_msaa", "get_msaa"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "msaa_2d", PROPERTY_HINT_ENUM, String::utf8("Disabled (Fastest),2× (Average),4× (Slow),8× (Slowest)")), "set_msaa_2d", "get_msaa_2d"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "msaa_3d", PROPERTY_HINT_ENUM, String::utf8("Disabled (Fastest),2× (Average),4× (Slow),8× (Slowest)")), "set_msaa_3d", "get_msaa_3d"); ADD_PROPERTY(PropertyInfo(Variant::INT, "screen_space_aa", PROPERTY_HINT_ENUM, "Disabled (Fastest),FXAA (Fast)"), "set_screen_space_aa", "get_screen_space_aa"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_taa"), "set_use_taa", "is_using_taa"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_debanding"), "set_use_debanding", "is_using_debanding"); @@ -3862,6 +3954,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"))); @@ -3953,9 +4046,9 @@ void Viewport::_bind_methods() { BIND_ENUM_CONSTANT(VRS_MAX); } -void Viewport::_validate_property(PropertyInfo &property) const { - if (vrs_mode != VRS_TEXTURE && (property.name == "vrs_texture")) { - property.usage = PROPERTY_USAGE_NO_EDITOR; +void Viewport::_validate_property(PropertyInfo &p_property) const { + if (vrs_mode != VRS_TEXTURE && (p_property.name == "vrs_texture")) { + p_property.usage = PROPERTY_USAGE_NO_EDITOR; } } @@ -3996,16 +4089,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. @@ -4078,10 +4165,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); } @@ -4089,7 +4176,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()) { @@ -4148,6 +4235,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 4221baff06..dc69ec24d8 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; @@ -297,7 +298,8 @@ private: bool positional_shadow_atlas_16_bits = true; PositionalShadowAtlasQuadrantSubdiv positional_shadow_atlas_quadrant_subdiv[4]; - MSAA msaa = MSAA_DISABLED; + MSAA msaa_2d = MSAA_DISABLED; + MSAA msaa_3d = MSAA_DISABLED; ScreenSpaceAA screen_space_aa = SCREEN_SPACE_AA_DISABLED; bool use_taa = false; @@ -315,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, @@ -401,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; @@ -460,6 +465,7 @@ private: void _sub_window_update(Window *p_window); void _sub_window_grab_focus(Window *p_window); void _sub_window_remove(Window *p_window); + int _sub_window_find(Window *p_window); bool _sub_windows_forward_input(const Ref<InputEvent> &p_event); SubWindowResize _sub_window_get_resize_margin(Window *p_subwindow, const Point2 &p_point); @@ -522,8 +528,11 @@ public: void set_positional_shadow_atlas_quadrant_subdiv(int p_quadrant, PositionalShadowAtlasQuadrantSubdiv p_subdiv); PositionalShadowAtlasQuadrantSubdiv get_positional_shadow_atlas_quadrant_subdiv(int p_quadrant) const; - void set_msaa(MSAA p_msaa); - MSAA get_msaa() const; + void set_msaa_2d(MSAA p_msaa); + MSAA get_msaa_2d() const; + + void set_msaa_3d(MSAA p_msaa); + MSAA get_msaa_3d() const; void set_screen_space_aa(ScreenSpaceAA p_screen_space_aa); ScreenSpaceAA get_screen_space_aa() const; @@ -576,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; @@ -633,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 @@ -709,7 +724,7 @@ public: bool is_using_xr(); #endif // _3D_DISABLED - virtual void _validate_property(PropertyInfo &property) const override; + void _validate_property(PropertyInfo &p_property) const; Viewport(); ~Viewport(); }; diff --git a/scene/main/window.cpp b/scene/main/window.cpp index d40b82f5eb..aff2c594d9 100644 --- a/scene/main/window.cpp +++ b/scene/main/window.cpp @@ -32,9 +32,13 @@ #include "core/config/project_settings.h" #include "core/debugger/engine_debugger.h" +#include "core/input/shortcut.h" #include "core/string/translation.h" +#include "core/variant/variant_parser.h" #include "scene/gui/control.h" #include "scene/scene_string_names.h" +#include "scene/theme/theme_db.h" +#include "scene/theme/theme_owner.h" void Window::set_title(const String &p_title) { title = p_title; @@ -153,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]; } @@ -228,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); @@ -255,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(); @@ -349,7 +350,9 @@ void Window::_event_callback(DisplayServer::WindowEvent p_event) { _propagate_window_notification(this, NOTIFICATION_WM_MOUSE_ENTER); emit_signal(SNAME("mouse_entered")); notification(NOTIFICATION_VP_MOUSE_ENTER); - DisplayServer::get_singleton()->cursor_set_shape(DisplayServer::CURSOR_ARROW); //restore cursor shape + if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_CURSOR_SHAPE)) { + DisplayServer::get_singleton()->cursor_set_shape(DisplayServer::CURSOR_ARROW); //restore cursor shape + } } break; case DisplayServer::WINDOW_EVENT_MOUSE_EXIT: { notification(NOTIFICATION_VP_MOUSE_EXIT); @@ -383,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); } @@ -444,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; @@ -498,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"); } @@ -547,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; @@ -585,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(); @@ -630,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 @@ -645,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) { @@ -653,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; @@ -731,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: { @@ -743,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); @@ -794,6 +808,19 @@ Viewport *Window::_get_embedder() const { void Window::_notification(int p_what) { switch (p_what) { + case NOTIFICATION_POSTINITIALIZE: { + _invalidate_theme_cache(); + _update_theme_item_cache(); + } break; + + case NOTIFICATION_PARENTED: { + theme_owner->assign_theme_on_parented(this); + } break; + + case NOTIFICATION_UNPARENTED: { + theme_owner->clear_theme_on_unparented(this); + } break; + case NOTIFICATION_ENTER_TREE: { bool embedded = false; { @@ -846,21 +873,13 @@ void Window::_notification(int p_what) { RS::get_singleton()->viewport_set_active(get_viewport_rid(), true); } - if (theme.is_null()) { - Control *parent_c = cast_to<Control>(get_parent()); - if (parent_c && (parent_c->data.theme_owner || parent_c->data.theme_owner_window)) { - theme_owner = parent_c->data.theme_owner; - theme_owner_window = parent_c->data.theme_owner_window; - notification(NOTIFICATION_THEME_CHANGED); - } else { - Window *parent_w = cast_to<Window>(get_parent()); - if (parent_w && (parent_w->theme_owner || parent_w->theme_owner_window)) { - theme_owner = parent_w->theme_owner; - theme_owner_window = parent_w->theme_owner_window; - notification(NOTIFICATION_THEME_CHANGED); - } - } - } + notification(NOTIFICATION_THEME_CHANGED); + } break; + + case NOTIFICATION_THEME_CHANGED: { + emit_signal(SceneStringNames::get_singleton()->theme_changed); + _invalidate_theme_cache(); + _update_theme_item_cache(); } break; case NOTIFICATION_READY: { @@ -870,6 +889,9 @@ void Window::_notification(int p_what) { } break; case NOTIFICATION_TRANSLATION_CHANGED: { + _invalidate_theme_cache(); + _update_theme_item_cache(); + if (embedder) { embedder->_sub_window_update(this); } else if (window_id != DisplayServer::INVALID_WINDOW_ID) { @@ -1027,9 +1049,31 @@ bool Window::_can_consume_input_events() const { void Window::_window_input(const Ref<InputEvent> &p_ev) { if (EngineDebugger::is_active()) { - // Quit from game window using F8. + // Quit from game window using the stop shortcut (F8 by default). + // The custom shortcut is provided via environment variable when running from the editor. + if (debugger_stop_shortcut.is_null()) { + String shortcut_str = OS::get_singleton()->get_environment("__GODOT_EDITOR_STOP_SHORTCUT__"); + if (!shortcut_str.is_empty()) { + Variant shortcut_var; + + VariantParser::StreamString ss; + ss.s = shortcut_str; + + String errs; + int line; + VariantParser::parse(&ss, shortcut_var, errs, line); + debugger_stop_shortcut = shortcut_var; + } + + if (debugger_stop_shortcut.is_null()) { + // Define a default shortcut if it wasn't provided or is invalid. + debugger_stop_shortcut.instantiate(); + debugger_stop_shortcut->set_events({ (Variant)InputEventKey::create_reference(Key::F8) }); + } + } + Ref<InputEventKey> k = p_ev; - if (k.is_valid() && k->is_pressed() && !k->is_echo() && k->get_keycode() == Key::F8) { + if (k.is_valid() && k->is_pressed() && !k->is_echo() && debugger_stop_shortcut->matches_event(k)) { EngineDebugger::get_singleton()->send_message("request_quit", Array()); } } @@ -1105,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); @@ -1131,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); @@ -1159,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); @@ -1212,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); } @@ -1234,53 +1291,41 @@ 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) { - Control *child_c = Object::cast_to<Control>(p_child); - - if (child_c && child_c->data.theme.is_null() && (theme_owner || theme_owner_window)) { - Control::_propagate_theme_changed(child_c, theme_owner, theme_owner_window); //need to propagate here, since many controls may require setting up stuff - } - - Window *child_w = Object::cast_to<Window>(p_child); - - if (child_w && child_w->theme.is_null() && (theme_owner || theme_owner_window)) { - Control::_propagate_theme_changed(child_w, theme_owner, theme_owner_window); //need to propagate here, since many controls may require setting up stuff - } - if (is_inside_tree() && wrap_controls) { child_controls_changed(); } } void Window::remove_child_notify(Node *p_child) { - Control *child_c = Object::cast_to<Control>(p_child); - - if (child_c && (child_c->data.theme_owner || child_c->data.theme_owner_window) && child_c->data.theme.is_null()) { - Control::_propagate_theme_changed(child_c, nullptr, nullptr); + if (is_inside_tree() && wrap_controls) { + child_controls_changed(); } +} - Window *child_w = Object::cast_to<Window>(p_child); +void Window::set_theme_owner_node(Node *p_node) { + theme_owner->set_owner_node(p_node); +} - if (child_w && (child_w->theme_owner || child_w->theme_owner_window) && child_w->theme.is_null()) { - Control::_propagate_theme_changed(child_w, nullptr, nullptr); - } +Node *Window::get_theme_owner_node() const { + return theme_owner->get_owner_node(); +} - if (is_inside_tree() && wrap_controls) { - child_controls_changed(); - } +bool Window::has_theme_owner_node() const { + return theme_owner->has_owner_node(); } void Window::set_theme(const Ref<Theme> &p_theme) { @@ -1288,134 +1333,183 @@ void Window::set_theme(const Ref<Theme> &p_theme) { return; } + if (theme.is_valid()) { + theme->disconnect("changed", callable_mp(this, &Window::_theme_changed)); + } + theme = p_theme; + if (theme.is_valid()) { + theme_owner->propagate_theme_changed(this, this, is_inside_tree(), true); + theme->connect("changed", callable_mp(this, &Window::_theme_changed), CONNECT_DEFERRED); + return; + } - if (!p_theme.is_null()) { - theme_owner = nullptr; - theme_owner_window = this; - Control::_propagate_theme_changed(this, nullptr, this); - } else { - Control *parent_c = cast_to<Control>(get_parent()); - if (parent_c && (parent_c->data.theme_owner || parent_c->data.theme_owner_window)) { - Control::_propagate_theme_changed(this, parent_c->data.theme_owner, parent_c->data.theme_owner_window); - } else { - Window *parent_w = cast_to<Window>(get_parent()); - if (parent_w && (parent_w->theme_owner || parent_w->theme_owner_window)) { - Control::_propagate_theme_changed(this, parent_w->theme_owner, parent_w->theme_owner_window); - } else { - Control::_propagate_theme_changed(this, nullptr, nullptr); - } - } + Control *parent_c = Object::cast_to<Control>(get_parent()); + if (parent_c && parent_c->has_theme_owner_node()) { + theme_owner->propagate_theme_changed(this, parent_c->get_theme_owner_node(), is_inside_tree(), true); + return; + } + + Window *parent_w = cast_to<Window>(get_parent()); + if (parent_w && parent_w->has_theme_owner_node()) { + theme_owner->propagate_theme_changed(this, parent_w->get_theme_owner_node(), is_inside_tree(), true); + return; } + + theme_owner->propagate_theme_changed(this, nullptr, is_inside_tree(), true); } Ref<Theme> Window::get_theme() const { return theme; } +void Window::_theme_changed() { + if (is_inside_tree()) { + theme_owner->propagate_theme_changed(this, this, true, false); + } +} + +void Window::_invalidate_theme_cache() { + theme_icon_cache.clear(); + theme_style_cache.clear(); + theme_font_cache.clear(); + theme_font_size_cache.clear(); + theme_color_cache.clear(); + theme_constant_cache.clear(); +} + +void Window::_update_theme_item_cache() { +} + void Window::set_theme_type_variation(const StringName &p_theme_type) { theme_type_variation = p_theme_type; - Control::_propagate_theme_changed(this, theme_owner, theme_owner_window); + if (is_inside_tree()) { + notification(NOTIFICATION_THEME_CHANGED); + } } StringName Window::get_theme_type_variation() const { return theme_type_variation; } -void Window::_get_theme_type_dependencies(const StringName &p_theme_type, List<StringName> *p_list) const { - if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == theme_type_variation) { - if (Theme::get_project_default().is_valid() && Theme::get_project_default()->get_type_variation_base(theme_type_variation) != StringName()) { - Theme::get_project_default()->get_type_dependencies(get_class_name(), theme_type_variation, p_list); - } else { - Theme::get_default()->get_type_dependencies(get_class_name(), theme_type_variation, p_list); - } - } else { - Theme::get_default()->get_type_dependencies(p_theme_type, StringName(), p_list); +Ref<Texture2D> Window::get_theme_icon(const StringName &p_name, const StringName &p_theme_type) const { + if (theme_icon_cache.has(p_theme_type) && theme_icon_cache[p_theme_type].has(p_name)) { + return theme_icon_cache[p_theme_type][p_name]; } -} -Ref<Texture2D> Window::get_theme_icon(const StringName &p_name, const StringName &p_theme_type) const { List<StringName> theme_types; - _get_theme_type_dependencies(p_theme_type, &theme_types); - return Control::get_theme_item_in_types<Ref<Texture2D>>(theme_owner, theme_owner_window, Theme::DATA_TYPE_ICON, p_name, theme_types); + theme_owner->get_theme_type_dependencies(this, p_theme_type, &theme_types); + Ref<Texture2D> icon = theme_owner->get_theme_item_in_types(Theme::DATA_TYPE_ICON, p_name, theme_types); + theme_icon_cache[p_theme_type][p_name] = icon; + return icon; } Ref<StyleBox> Window::get_theme_stylebox(const StringName &p_name, const StringName &p_theme_type) const { + if (theme_style_cache.has(p_theme_type) && theme_style_cache[p_theme_type].has(p_name)) { + return theme_style_cache[p_theme_type][p_name]; + } + List<StringName> theme_types; - _get_theme_type_dependencies(p_theme_type, &theme_types); - return Control::get_theme_item_in_types<Ref<StyleBox>>(theme_owner, theme_owner_window, Theme::DATA_TYPE_STYLEBOX, p_name, theme_types); + theme_owner->get_theme_type_dependencies(this, p_theme_type, &theme_types); + Ref<StyleBox> style = theme_owner->get_theme_item_in_types(Theme::DATA_TYPE_STYLEBOX, p_name, theme_types); + theme_style_cache[p_theme_type][p_name] = style; + return style; } Ref<Font> Window::get_theme_font(const StringName &p_name, const StringName &p_theme_type) const { + if (theme_font_cache.has(p_theme_type) && theme_font_cache[p_theme_type].has(p_name)) { + return theme_font_cache[p_theme_type][p_name]; + } + List<StringName> theme_types; - _get_theme_type_dependencies(p_theme_type, &theme_types); - return Control::get_theme_item_in_types<Ref<Font>>(theme_owner, theme_owner_window, Theme::DATA_TYPE_FONT, p_name, theme_types); + theme_owner->get_theme_type_dependencies(this, p_theme_type, &theme_types); + Ref<Font> font = theme_owner->get_theme_item_in_types(Theme::DATA_TYPE_FONT, p_name, theme_types); + theme_font_cache[p_theme_type][p_name] = font; + return font; } int Window::get_theme_font_size(const StringName &p_name, const StringName &p_theme_type) const { + if (theme_font_size_cache.has(p_theme_type) && theme_font_size_cache[p_theme_type].has(p_name)) { + return theme_font_size_cache[p_theme_type][p_name]; + } + List<StringName> theme_types; - _get_theme_type_dependencies(p_theme_type, &theme_types); - return Control::get_theme_item_in_types<int>(theme_owner, theme_owner_window, Theme::DATA_TYPE_FONT_SIZE, p_name, theme_types); + theme_owner->get_theme_type_dependencies(this, p_theme_type, &theme_types); + int font_size = theme_owner->get_theme_item_in_types(Theme::DATA_TYPE_FONT_SIZE, p_name, theme_types); + theme_font_size_cache[p_theme_type][p_name] = font_size; + return font_size; } Color Window::get_theme_color(const StringName &p_name, const StringName &p_theme_type) const { + if (theme_color_cache.has(p_theme_type) && theme_color_cache[p_theme_type].has(p_name)) { + return theme_color_cache[p_theme_type][p_name]; + } + List<StringName> theme_types; - _get_theme_type_dependencies(p_theme_type, &theme_types); - return Control::get_theme_item_in_types<Color>(theme_owner, theme_owner_window, Theme::DATA_TYPE_COLOR, p_name, theme_types); + theme_owner->get_theme_type_dependencies(this, p_theme_type, &theme_types); + Color color = theme_owner->get_theme_item_in_types(Theme::DATA_TYPE_COLOR, p_name, theme_types); + theme_color_cache[p_theme_type][p_name] = color; + return color; } int Window::get_theme_constant(const StringName &p_name, const StringName &p_theme_type) const { + if (theme_constant_cache.has(p_theme_type) && theme_constant_cache[p_theme_type].has(p_name)) { + return theme_constant_cache[p_theme_type][p_name]; + } + List<StringName> theme_types; - _get_theme_type_dependencies(p_theme_type, &theme_types); - return Control::get_theme_item_in_types<int>(theme_owner, theme_owner_window, Theme::DATA_TYPE_CONSTANT, p_name, theme_types); + theme_owner->get_theme_type_dependencies(this, p_theme_type, &theme_types); + int constant = theme_owner->get_theme_item_in_types(Theme::DATA_TYPE_CONSTANT, p_name, theme_types); + theme_constant_cache[p_theme_type][p_name] = constant; + return constant; } bool Window::has_theme_icon(const StringName &p_name, const StringName &p_theme_type) const { List<StringName> theme_types; - _get_theme_type_dependencies(p_theme_type, &theme_types); - return Control::has_theme_item_in_types(theme_owner, theme_owner_window, Theme::DATA_TYPE_ICON, p_name, theme_types); + theme_owner->get_theme_type_dependencies(this, p_theme_type, &theme_types); + return theme_owner->has_theme_item_in_types(Theme::DATA_TYPE_ICON, p_name, theme_types); } bool Window::has_theme_stylebox(const StringName &p_name, const StringName &p_theme_type) const { List<StringName> theme_types; - _get_theme_type_dependencies(p_theme_type, &theme_types); - return Control::has_theme_item_in_types(theme_owner, theme_owner_window, Theme::DATA_TYPE_STYLEBOX, p_name, theme_types); + theme_owner->get_theme_type_dependencies(this, p_theme_type, &theme_types); + return theme_owner->has_theme_item_in_types(Theme::DATA_TYPE_STYLEBOX, p_name, theme_types); } bool Window::has_theme_font(const StringName &p_name, const StringName &p_theme_type) const { List<StringName> theme_types; - _get_theme_type_dependencies(p_theme_type, &theme_types); - return Control::has_theme_item_in_types(theme_owner, theme_owner_window, Theme::DATA_TYPE_FONT, p_name, theme_types); + theme_owner->get_theme_type_dependencies(this, p_theme_type, &theme_types); + return theme_owner->has_theme_item_in_types(Theme::DATA_TYPE_FONT, p_name, theme_types); } bool Window::has_theme_font_size(const StringName &p_name, const StringName &p_theme_type) const { List<StringName> theme_types; - _get_theme_type_dependencies(p_theme_type, &theme_types); - return Control::has_theme_item_in_types(theme_owner, theme_owner_window, Theme::DATA_TYPE_FONT_SIZE, p_name, theme_types); + theme_owner->get_theme_type_dependencies(this, p_theme_type, &theme_types); + return theme_owner->has_theme_item_in_types(Theme::DATA_TYPE_FONT_SIZE, p_name, theme_types); } bool Window::has_theme_color(const StringName &p_name, const StringName &p_theme_type) const { List<StringName> theme_types; - _get_theme_type_dependencies(p_theme_type, &theme_types); - return Control::has_theme_item_in_types(theme_owner, theme_owner_window, Theme::DATA_TYPE_COLOR, p_name, theme_types); + theme_owner->get_theme_type_dependencies(this, p_theme_type, &theme_types); + return theme_owner->has_theme_item_in_types(Theme::DATA_TYPE_COLOR, p_name, theme_types); } bool Window::has_theme_constant(const StringName &p_name, const StringName &p_theme_type) const { List<StringName> theme_types; - _get_theme_type_dependencies(p_theme_type, &theme_types); - return Control::has_theme_item_in_types(theme_owner, theme_owner_window, Theme::DATA_TYPE_CONSTANT, p_name, theme_types); + theme_owner->get_theme_type_dependencies(this, p_theme_type, &theme_types); + return theme_owner->has_theme_item_in_types(Theme::DATA_TYPE_CONSTANT, p_name, theme_types); } float Window::get_theme_default_base_scale() const { - return Control::fetch_theme_default_base_scale(theme_owner, theme_owner_window); + return theme_owner->get_theme_default_base_scale(); } Ref<Font> Window::get_theme_default_font() const { - return Control::fetch_theme_default_font(theme_owner, theme_owner_window); + return theme_owner->get_theme_default_font(); } int Window::get_theme_default_font_size() const { - return Control::fetch_theme_default_font_size(theme_owner, theme_owner_window); + return theme_owner->get_theme_default_font_size(); } Rect2i Window::get_parent_rect() const { @@ -1475,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; @@ -1510,15 +1604,15 @@ bool Window::is_auto_translating() const { return auto_translate; } -void Window::_validate_property(PropertyInfo &property) const { - if (property.name == "theme_type_variation") { +void Window::_validate_property(PropertyInfo &p_property) const { + if (p_property.name == "theme_type_variation") { List<StringName> names; // Only the default theme and the project theme are used for the list of options. // This is an imposed limitation to simplify the logic needed to leverage those options. - Theme::get_default()->get_type_variation_list(get_class_name(), &names); - if (Theme::get_project_default().is_valid()) { - Theme::get_project_default()->get_type_variation_list(get_class_name(), &names); + ThemeDB::get_singleton()->get_default_theme()->get_type_variation_list(get_class_name(), &names); + if (ThemeDB::get_singleton()->get_project_theme().is_valid()) { + ThemeDB::get_singleton()->get_project_theme()->get_type_variation_list(get_class_name(), &names); } names.sort_custom<StringName::AlphCompare>(); @@ -1534,12 +1628,12 @@ void Window::_validate_property(PropertyInfo &property) const { unique_names.append(E); } - property.hint_string = hint_string; + p_property.hint_string = hint_string; } } 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; @@ -1679,6 +1773,7 @@ void Window::_bind_methods() { ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "transparent"), "set_flag", "get_flag", FLAG_TRANSPARENT); ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "unfocusable"), "set_flag", "get_flag", FLAG_NO_FOCUS); ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "popup_window"), "set_flag", "get_flag", FLAG_POPUP); + ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "extend_to_title"), "set_flag", "get_flag", FLAG_EXTEND_TO_TITLE); ADD_GROUP("Limits", ""); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "min_size", PROPERTY_HINT_NONE, "suffix:px"), "set_min_size", "get_min_size"); @@ -1708,8 +1803,10 @@ 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); BIND_ENUM_CONSTANT(MODE_WINDOWED); BIND_ENUM_CONSTANT(MODE_MINIMIZED); @@ -1723,6 +1820,7 @@ void Window::_bind_methods() { BIND_ENUM_CONSTANT(FLAG_TRANSPARENT); BIND_ENUM_CONSTANT(FLAG_NO_FOCUS); BIND_ENUM_CONSTANT(FLAG_POPUP); + BIND_ENUM_CONSTANT(FLAG_EXTEND_TO_TITLE); BIND_ENUM_CONSTANT(FLAG_MAX); BIND_ENUM_CONSTANT(CONTENT_SCALE_MODE_DISABLED); @@ -1742,8 +1840,15 @@ 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); } Window::~Window() { + memdelete(theme_owner); } diff --git a/scene/main/window.h b/scene/main/window.h index c060f1d79d..03597b309a 100644 --- a/scene/main/window.h +++ b/scene/main/window.h @@ -32,11 +32,13 @@ #define WINDOW_H #include "scene/main/viewport.h" +#include "scene/resources/theme.h" class Control; class Font; +class Shortcut; class StyleBox; -class Theme; +class ThemeOwner; class Window : public Viewport { GDCLASS(Window, Viewport) @@ -56,6 +58,7 @@ public: FLAG_TRANSPARENT = DisplayServer::WINDOW_FLAG_TRANSPARENT, FLAG_NO_FOCUS = DisplayServer::WINDOW_FLAG_NO_FOCUS, FLAG_POPUP = DisplayServer::WINDOW_FLAG_POPUP, + FLAG_EXTEND_TO_TITLE = DisplayServer::WINDOW_FLAG_EXTEND_TO_TITLE, FLAG_MAX = DisplayServer::WINDOW_FLAG_MAX, }; @@ -133,12 +136,20 @@ private: Window *exclusive_child = nullptr; HashSet<Window *> transient_children; - friend class Control; + ThemeOwner *theme_owner = nullptr; Ref<Theme> theme; - Control *theme_owner = nullptr; - Window *theme_owner_window = nullptr; StringName theme_type_variation; + mutable HashMap<StringName, Theme::ThemeIconMap> theme_icon_cache; + mutable HashMap<StringName, Theme::ThemeStyleMap> theme_style_cache; + mutable HashMap<StringName, Theme::ThemeFontMap> theme_font_cache; + mutable HashMap<StringName, Theme::ThemeFontSizeMap> theme_font_size_cache; + mutable HashMap<StringName, Theme::ThemeColorMap> theme_color_cache; + mutable HashMap<StringName, Theme::ThemeConstantMap> theme_constant_cache; + + void _theme_changed(); + void _invalidate_theme_cache(); + Viewport *embedder = nullptr; friend class Viewport; //friend back, can call the methods below @@ -150,15 +161,19 @@ private: void _event_callback(DisplayServer::WindowEvent p_event); virtual bool _can_consume_input_events() const override; + Ref<Shortcut> debugger_stop_shortcut; + protected: Viewport *_get_embedder() const; virtual Rect2i _popup_adjust_rect() const { return Rect2i(); } + virtual void _update_theme_item_cache(); + virtual void _post_popup() {} virtual Size2 _get_contents_minimum_size() const; static void _bind_methods(); void _notification(int p_what); - virtual void _validate_property(PropertyInfo &property) const override; + void _validate_property(PropertyInfo &p_property) const; virtual void add_child_notify(Node *p_child) override; virtual void remove_child_notify(Node *p_child) override; @@ -205,6 +220,8 @@ public: void set_visible(bool p_visible); bool is_visible() const; + void update_mouse_cursor_shape(); + void show(); void hide(); @@ -217,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); @@ -251,12 +270,15 @@ public: void popup_centered(const Size2i &p_minsize = Size2i()); void popup_centered_clamped(const Size2i &p_size = Size2i(), float p_fallback_ratio = 0.75); + void set_theme_owner_node(Node *p_node); + Node *get_theme_owner_node() const; + bool has_theme_owner_node() const; + void set_theme(const Ref<Theme> &p_theme); Ref<Theme> get_theme() const; void set_theme_type_variation(const StringName &p_theme_type); StringName get_theme_type_variation() const; - _FORCE_INLINE_ void _get_theme_type_dependencies(const StringName &p_theme_type, List<StringName> *p_list) const; Size2 get_contents_minimum_size() const; |