summaryrefslogtreecommitdiff
path: root/scene/main
diff options
context:
space:
mode:
Diffstat (limited to 'scene/main')
-rw-r--r--scene/main/canvas_item.cpp122
-rw-r--r--scene/main/canvas_item.h32
-rw-r--r--scene/main/canvas_layer.cpp12
-rw-r--r--scene/main/canvas_layer.h2
-rw-r--r--scene/main/http_request.cpp149
-rw-r--r--scene/main/http_request.h20
-rw-r--r--scene/main/missing_node.cpp4
-rw-r--r--scene/main/missing_node.h2
-rw-r--r--scene/main/multiplayer_api.cpp416
-rw-r--r--scene/main/multiplayer_api.h115
-rw-r--r--scene/main/multiplayer_peer.cpp223
-rw-r--r--scene/main/multiplayer_peer.h146
-rw-r--r--scene/main/node.cpp298
-rw-r--r--scene/main/node.h78
-rw-r--r--scene/main/resource_preloader.cpp2
-rw-r--r--scene/main/scene_tree.cpp334
-rw-r--r--scene/main/scene_tree.h29
-rw-r--r--scene/main/shader_globals_override.cpp26
-rw-r--r--scene/main/shader_globals_override.h2
-rw-r--r--scene/main/timer.cpp4
-rw-r--r--scene/main/timer.h2
-rw-r--r--scene/main/viewport.cpp314
-rw-r--r--scene/main/viewport.h24
-rw-r--r--scene/main/window.cpp462
-rw-r--r--scene/main/window.h34
25 files changed, 1999 insertions, 853 deletions
diff --git a/scene/main/canvas_item.cpp b/scene/main/canvas_item.cpp
index 2cd7cf5648..840913d466 100644
--- a/scene/main/canvas_item.cpp
+++ b/scene/main/canvas_item.cpp
@@ -64,9 +64,6 @@ void CanvasItem::_propagate_visibility_changed(bool p_parent_visible_in_tree) {
if (!visible) {
return;
}
- if (p_parent_visible_in_tree && first_draw) { // Avoid propagating it twice.
- first_draw = false;
- }
_handle_visibility_change(p_parent_visible_in_tree);
}
@@ -91,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);
}
@@ -124,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;
@@ -133,10 +130,6 @@ void CanvasItem::_update_callback() {
RenderingServer::get_singleton()->canvas_item_clear(get_canvas_item());
//todo updating = true - only allow drawing here
if (is_visible_in_tree()) {
- if (first_draw) {
- notification(NOTIFICATION_VISIBILITY_CHANGED);
- first_draw = false;
- }
drawing = true;
current_item_drawn = this;
notification(NOTIFICATION_DRAW);
@@ -230,16 +223,16 @@ void CanvasItem::_enter_canvas() {
RenderingServer::get_singleton()->canvas_item_set_parent(canvas_item, canvas);
- group = "root_canvas" + itos(canvas.get_id());
+ canvas_group = "root_canvas" + itos(canvas.get_id());
- add_to_group(group);
+ add_to_group(canvas_group);
if (canvas_layer) {
canvas_layer->reset_sort_index();
} else {
get_viewport()->gui_reset_canvas_sort_index();
}
- get_tree()->call_group_flags(SceneTree::GROUP_CALL_UNIQUE | SceneTree::GROUP_CALL_DEFERRED, group, SNAME("_top_level_raise_self"));
+ get_tree()->call_group_flags(SceneTree::GROUP_CALL_UNIQUE | SceneTree::GROUP_CALL_DEFERRED, canvas_group, SNAME("_top_level_raise_self"));
} else {
CanvasItem *parent = get_parent_item();
@@ -249,7 +242,7 @@ void CanvasItem::_enter_canvas() {
}
pending_update = false;
- update();
+ queue_redraw();
notification(NOTIFICATION_ENTER_CANVAS);
}
@@ -258,14 +251,16 @@ void CanvasItem::_exit_canvas() {
notification(NOTIFICATION_EXIT_CANVAS, true); //reverse the notification
RenderingServer::get_singleton()->canvas_item_set_parent(canvas_item, RID());
canvas_layer = nullptr;
- group = StringName();
+ if (canvas_group != StringName()) {
+ remove_from_group(canvas_group);
+ canvas_group = StringName();
+ }
}
void CanvasItem::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE: {
ERR_FAIL_COND(!is_inside_tree());
- first_draw = true;
Node *parent = get_parent();
if (parent) {
@@ -304,6 +299,10 @@ void CanvasItem::_notification(int p_what) {
}
}
+ RenderingServer::get_singleton()->canvas_item_set_visible(canvas_item, is_visible_in_tree()); // The visibility of the parent may change.
+ if (is_visible_in_tree()) {
+ notification(NOTIFICATION_VISIBILITY_CHANGED); // Considered invisible until entered.
+ }
_enter_canvas();
_update_texture_filter_changed(false);
@@ -319,8 +318,8 @@ void CanvasItem::_notification(int p_what) {
break;
}
- if (group != StringName()) {
- get_tree()->call_group_flags(SceneTree::GROUP_CALL_UNIQUE | SceneTree::GROUP_CALL_DEFERRED, group, "_top_level_raise_self");
+ if (canvas_group != StringName()) {
+ get_tree()->call_group_flags(SceneTree::GROUP_CALL_UNIQUE | SceneTree::GROUP_CALL_DEFERRED, canvas_group, "_top_level_raise_self");
} else {
CanvasItem *p = get_parent_item();
ERR_FAIL_COND(!p);
@@ -339,6 +338,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;
@@ -356,7 +356,7 @@ void CanvasItem::_window_visibility_changed() {
}
}
-void CanvasItem::update() {
+void CanvasItem::queue_redraw() {
if (!is_inside_tree()) {
return;
}
@@ -366,7 +366,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) {
@@ -439,7 +446,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);
}
@@ -588,6 +595,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.");
@@ -862,7 +875,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);
@@ -891,7 +903,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);
@@ -920,6 +933,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>()));
@@ -972,8 +986,8 @@ void CanvasItem::_bind_methods() {
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);
@@ -983,11 +997,11 @@ 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_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", "");
@@ -1021,6 +1035,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 {
@@ -1078,7 +1097,7 @@ int CanvasItem::get_canvas_layer() const {
}
}
-void CanvasItem::_update_texture_filter_changed(bool p_propagate) {
+void CanvasItem::_refresh_texture_filter_cache() {
if (!is_inside_tree()) {
return;
}
@@ -1093,8 +1112,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) {
@@ -1119,7 +1146,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;
}
@@ -1134,8 +1161,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) {
@@ -1155,26 +1190,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();
@@ -1327,7 +1375,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 a4574dce61..565ea930ce 100644
--- a/scene/main/canvas_item.h
+++ b/scene/main/canvas_item.h
@@ -66,11 +66,19 @@ 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 group;
+ StringName canvas_group;
CanvasLayer *canvas_layer = nullptr;
@@ -83,10 +91,8 @@ private:
int light_mask = 1;
Window *window = nullptr;
- bool first_draw = false;
bool visible = true;
bool parent_visible_in_tree = false;
- bool clip_children = false;
bool pending_update = false;
bool top_level = false;
bool drawing = false;
@@ -96,6 +102,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;
@@ -111,7 +119,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();
@@ -122,7 +130,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:
@@ -198,10 +208,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;
@@ -227,6 +238,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>());
@@ -309,6 +321,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); };
@@ -320,6 +335,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..214efe432b 100644
--- a/scene/main/canvas_layer.cpp
+++ b/scene/main/canvas_layer.cpp
@@ -286,9 +286,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 +329,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 49b4b1b30c..80445684b0 100644
--- a/scene/main/http_request.h
+++ b/scene/main/http_request.h
@@ -28,10 +28,11 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef HTTPREQUEST_H
-#define HTTPREQUEST_H
+#ifndef HTTP_REQUEST_H
+#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;
@@ -162,4 +166,4 @@ public:
VARIANT_ENUM_CAST(HTTPRequest::Result);
-#endif // HTTPREQUEST_H
+#endif // HTTP_REQUEST_H
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
new file mode 100644
index 0000000000..7e2c82c88d
--- /dev/null
+++ b/scene/main/multiplayer_api.cpp
@@ -0,0 +1,416 @@
+/*************************************************************************/
+/* multiplayer_api.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "multiplayer_api.h"
+
+#include "core/debugger/engine_debugger.h"
+#include "core/io/marshalls.h"
+
+#include <stdint.h>
+
+#ifdef DEBUG_ENABLED
+#include "core/os/os.h"
+#endif
+
+StringName MultiplayerAPI::default_interface = StringName();
+
+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));
+ default_interface = p_interface;
+}
+
+StringName MultiplayerAPI::get_default_interface() {
+ return default_interface;
+}
+
+Ref<MultiplayerAPI> MultiplayerAPI::create_default_interface() {
+ if (default_interface != StringName()) {
+ return Ref<MultiplayerAPI>(Object::cast_to<MultiplayerAPI>(ClassDB::instantiate(default_interface)));
+ }
+ return Ref<MultiplayerAPI>(memnew(MultiplayerAPIExtension));
+}
+
+// The variant is compressed and encoded; The first byte contains all the meta
+// information and the format is:
+// - The first LSB 6 bits are used for the variant type.
+// - The next two bits are used to store the encoding mode.
+// - 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 << 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` == 38 and `ENCODE_VARIANT_MASK` == 77
+ CRASH_COND(p_variant.get_type() > VARIANT_META_TYPE_MASK);
+
+ uint8_t *buf = r_buffer;
+ r_len = 0;
+ uint8_t encode_mode = 0;
+
+ switch (p_variant.get_type()) {
+ case Variant::BOOL: {
+ if (buf) {
+ // 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] |= p_variant.get_type();
+ }
+ r_len += 1;
+ } break;
+ case Variant::INT: {
+ if (buf) {
+ // Reserve the first byte for the meta.
+ buf += 1;
+ }
+ r_len += 1;
+ int64_t val = p_variant;
+ if (val <= (int64_t)INT8_MAX && val >= (int64_t)INT8_MIN) {
+ // Use 8 bit
+ encode_mode = ENCODE_8;
+ if (buf) {
+ buf[0] = val;
+ }
+ r_len += 1;
+ } else if (val <= (int64_t)INT16_MAX && val >= (int64_t)INT16_MIN) {
+ // Use 16 bit
+ encode_mode = ENCODE_16;
+ if (buf) {
+ encode_uint16(val, buf);
+ }
+ r_len += 2;
+ } else if (val <= (int64_t)INT32_MAX && val >= (int64_t)INT32_MIN) {
+ // Use 32 bit
+ encode_mode = ENCODE_32;
+ if (buf) {
+ encode_uint32(val, buf);
+ }
+ r_len += 4;
+ } else {
+ // Use 64 bit
+ encode_mode = ENCODE_64;
+ if (buf) {
+ encode_uint64(val, buf);
+ }
+ r_len += 8;
+ }
+ // Store the meta
+ if (buf) {
+ buf -= 1;
+ buf[0] = encode_mode | p_variant.get_type();
+ }
+ } break;
+ default:
+ // Any other case is not yet compressed.
+ Error err = encode_variant(p_variant, r_buffer, r_len, p_allow_object_decoding);
+ if (err != OK) {
+ return err;
+ }
+ if (r_buffer) {
+ // The first byte is not used by the marshalling, so store the type
+ // so we know how to decompress and decode this variant.
+ r_buffer[0] = p_variant.get_type();
+ }
+ }
+
+ return OK;
+}
+
+Error MultiplayerAPI::decode_and_decompress_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int *r_len, bool p_allow_object_decoding) {
+ const uint8_t *buf = p_buffer;
+ int len = p_len;
+
+ ERR_FAIL_COND_V(len < 1, ERR_INVALID_DATA);
+ uint8_t type = buf[0] & VARIANT_META_TYPE_MASK;
+ uint8_t encode_mode = buf[0] & VARIANT_META_EMODE_MASK;
+
+ ERR_FAIL_COND_V(type >= Variant::VARIANT_MAX, ERR_INVALID_DATA);
+
+ switch (type) {
+ case Variant::BOOL: {
+ bool val = (buf[0] & VARIANT_META_BOOL_MASK) > 0;
+ r_variant = val;
+ if (r_len) {
+ *r_len = 1;
+ }
+ } break;
+ case Variant::INT: {
+ buf += 1;
+ len -= 1;
+ if (r_len) {
+ *r_len = 1;
+ }
+ if (encode_mode == ENCODE_8) {
+ // 8 bits.
+ ERR_FAIL_COND_V(len < 1, ERR_INVALID_DATA);
+ int8_t val = buf[0];
+ r_variant = val;
+ if (r_len) {
+ (*r_len) += 1;
+ }
+ } else if (encode_mode == ENCODE_16) {
+ // 16 bits.
+ ERR_FAIL_COND_V(len < 2, ERR_INVALID_DATA);
+ int16_t val = decode_uint16(buf);
+ r_variant = val;
+ if (r_len) {
+ (*r_len) += 2;
+ }
+ } else if (encode_mode == ENCODE_32) {
+ // 32 bits.
+ ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA);
+ int32_t val = decode_uint32(buf);
+ r_variant = val;
+ if (r_len) {
+ (*r_len) += 4;
+ }
+ } else {
+ // 64 bits.
+ ERR_FAIL_COND_V(len < 8, ERR_INVALID_DATA);
+ int64_t val = decode_uint64(buf);
+ r_variant = val;
+ if (r_len) {
+ (*r_len) += 8;
+ }
+ }
+ } break;
+ default:
+ Error err = decode_variant(r_variant, p_buffer, p_len, r_len, p_allow_object_decoding);
+ if (err != OK) {
+ return err;
+ }
+ }
+
+ return OK;
+}
+
+Error MultiplayerAPI::encode_and_compress_variants(const Variant **p_variants, int p_count, uint8_t *p_buffer, int &r_len, bool *r_raw, bool p_allow_object_decoding) {
+ r_len = 0;
+ int size = 0;
+
+ if (p_count == 0) {
+ if (r_raw) {
+ *r_raw = true;
+ }
+ return OK;
+ }
+
+ // Try raw encoding optimization.
+ if (r_raw && p_count == 1) {
+ *r_raw = false;
+ const Variant &v = *(p_variants[0]);
+ if (v.get_type() == Variant::PACKED_BYTE_ARRAY) {
+ *r_raw = true;
+ const PackedByteArray pba = v;
+ if (p_buffer) {
+ memcpy(p_buffer, pba.ptr(), pba.size());
+ }
+ r_len += pba.size();
+ } else {
+ encode_and_compress_variant(v, p_buffer, size, p_allow_object_decoding);
+ r_len += size;
+ }
+ return OK;
+ }
+
+ // Regular encoding.
+ for (int i = 0; i < p_count; i++) {
+ const Variant &v = *(p_variants[i]);
+ encode_and_compress_variant(v, p_buffer ? p_buffer + r_len : nullptr, size, p_allow_object_decoding);
+ r_len += size;
+ }
+ return OK;
+}
+
+Error MultiplayerAPI::decode_and_decompress_variants(Vector<Variant> &r_variants, const uint8_t *p_buffer, int p_len, int &r_len, bool p_raw, bool p_allow_object_decoding) {
+ r_len = 0;
+ int argc = r_variants.size();
+ if (argc == 0 && p_raw) {
+ return OK;
+ }
+ ERR_FAIL_COND_V(p_raw && argc != 1, ERR_INVALID_DATA);
+ if (p_raw) {
+ r_len = p_len;
+ PackedByteArray pba;
+ pba.resize(p_len);
+ memcpy(pba.ptrw(), p_buffer, p_len);
+ r_variants.write[0] = pba;
+ return OK;
+ }
+
+ Vector<Variant> args;
+ Vector<const Variant *> argp;
+ args.resize(argc);
+
+ for (int i = 0; i < argc; i++) {
+ ERR_FAIL_COND_V_MSG(r_len >= p_len, ERR_INVALID_DATA, "Invalid packet received. Size too small.");
+
+ int vlen;
+ Error err = MultiplayerAPI::decode_and_decompress_variant(r_variants.write[i], &p_buffer[r_len], p_len - r_len, &vlen, p_allow_object_decoding);
+ ERR_FAIL_COND_V_MSG(err != OK, err, "Invalid packet received. Unable to decode state variable.");
+ r_len += vlen;
+ }
+ return OK;
+}
+
+Error MultiplayerAPI::_rpc_bind(int p_peer, Object *p_object, const StringName &p_method, Array p_args) {
+ Vector<Variant> args;
+ Vector<const Variant *> argsp;
+ args.resize(p_args.size());
+ argsp.resize(p_args.size());
+ Variant *ptr = args.ptrw();
+ const Variant **pptr = argsp.ptrw();
+ for (int i = 0; i < p_args.size(); i++) {
+ ptr[i] = p_args[i];
+ pptr[i] = &ptr[i];
+ }
+ return rpcp(p_object, p_peer, p_method, argsp.size() ? argsp.ptrw() : nullptr, argsp.size());
+}
+
+void MultiplayerAPI::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("has_multiplayer_peer"), &MultiplayerAPI::has_multiplayer_peer);
+ ClassDB::bind_method(D_METHOD("get_multiplayer_peer"), &MultiplayerAPI::get_multiplayer_peer);
+ ClassDB::bind_method(D_METHOD("set_multiplayer_peer", "peer"), &MultiplayerAPI::set_multiplayer_peer);
+ ClassDB::bind_method(D_METHOD("get_unique_id"), &MultiplayerAPI::get_unique_id);
+ ClassDB::bind_method(D_METHOD("is_server"), &MultiplayerAPI::is_server);
+ ClassDB::bind_method(D_METHOD("get_remote_sender_id"), &MultiplayerAPI::get_remote_sender_id);
+ ClassDB::bind_method(D_METHOD("poll"), &MultiplayerAPI::poll);
+ ClassDB::bind_method(D_METHOD("rpc", "peer", "object", "method", "arguments"), &MultiplayerAPI::_rpc_bind, DEFVAL(Array()));
+ ClassDB::bind_method(D_METHOD("object_configuration_add", "object", "configuration"), &MultiplayerAPI::object_configuration_add);
+ ClassDB::bind_method(D_METHOD("object_configuration_remove", "object", "configuration"), &MultiplayerAPI::object_configuration_remove);
+
+ ClassDB::bind_method(D_METHOD("get_peers"), &MultiplayerAPI::get_peer_ids);
+
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "multiplayer_peer", PROPERTY_HINT_RESOURCE_TYPE, "MultiplayerPeer", PROPERTY_USAGE_NONE), "set_multiplayer_peer", "get_multiplayer_peer");
+
+ ClassDB::bind_static_method("MultiplayerAPI", D_METHOD("set_default_interface", "interface_name"), &MultiplayerAPI::set_default_interface);
+ ClassDB::bind_static_method("MultiplayerAPI", D_METHOD("get_default_interface"), &MultiplayerAPI::get_default_interface);
+ ClassDB::bind_static_method("MultiplayerAPI", D_METHOD("create_default_interface"), &MultiplayerAPI::create_default_interface);
+
+ ADD_SIGNAL(MethodInfo("peer_connected", PropertyInfo(Variant::INT, "id")));
+ ADD_SIGNAL(MethodInfo("peer_disconnected", PropertyInfo(Variant::INT, "id")));
+ ADD_SIGNAL(MethodInfo("connected_to_server"));
+ ADD_SIGNAL(MethodInfo("connection_failed"));
+ ADD_SIGNAL(MethodInfo("server_disconnected"));
+
+ BIND_ENUM_CONSTANT(RPC_MODE_DISABLED);
+ BIND_ENUM_CONSTANT(RPC_MODE_ANY_PEER);
+ BIND_ENUM_CONSTANT(RPC_MODE_AUTHORITY);
+}
+
+/// MultiplayerAPIExtension
+
+Error MultiplayerAPIExtension::poll() {
+ int err;
+ if (GDVIRTUAL_CALL(_poll, err)) {
+ return (Error)err;
+ }
+ return OK;
+}
+
+void MultiplayerAPIExtension::set_multiplayer_peer(const Ref<MultiplayerPeer> &p_peer) {
+ GDVIRTUAL_CALL(_set_multiplayer_peer, p_peer);
+}
+
+Ref<MultiplayerPeer> MultiplayerAPIExtension::get_multiplayer_peer() {
+ Ref<MultiplayerPeer> peer;
+ if (GDVIRTUAL_CALL(_get_multiplayer_peer, peer)) {
+ return peer;
+ }
+ return nullptr;
+}
+
+int MultiplayerAPIExtension::get_unique_id() {
+ int id;
+ if (GDVIRTUAL_CALL(_get_unique_id, id)) {
+ return id;
+ }
+ return 1;
+}
+
+Vector<int> MultiplayerAPIExtension::get_peer_ids() {
+ Vector<int> ids;
+ if (GDVIRTUAL_CALL(_get_peer_ids, ids)) {
+ return ids;
+ }
+ return Vector<int>();
+}
+
+Error MultiplayerAPIExtension::rpcp(Object *p_obj, int p_peer_id, const StringName &p_method, const Variant **p_arg, int p_argcount) {
+ if (!GDVIRTUAL_IS_OVERRIDDEN(_rpc)) {
+ return ERR_UNAVAILABLE;
+ }
+ Array args;
+ for (int i = 0; i < p_argcount; i++) {
+ args.push_back(*p_arg[i]);
+ }
+ int ret = FAILED;
+ if (GDVIRTUAL_CALL(_rpc, p_peer_id, p_obj, p_method, args, ret)) {
+ return (Error)ret;
+ }
+ return FAILED;
+}
+
+int MultiplayerAPIExtension::get_remote_sender_id() {
+ int id = 0;
+ if (GDVIRTUAL_CALL(_get_remote_sender_id, id)) {
+ return id;
+ }
+ return 0;
+}
+
+Error MultiplayerAPIExtension::object_configuration_add(Object *p_object, Variant p_config) {
+ int err = ERR_UNAVAILABLE;
+ if (GDVIRTUAL_CALL(_object_configuration_add, p_object, p_config, err)) {
+ return (Error)err;
+ }
+ return ERR_UNAVAILABLE;
+}
+
+Error MultiplayerAPIExtension::object_configuration_remove(Object *p_object, Variant p_config) {
+ int err = ERR_UNAVAILABLE;
+ if (GDVIRTUAL_CALL(_object_configuration_remove, p_object, p_config, err)) {
+ return (Error)err;
+ }
+ return ERR_UNAVAILABLE;
+}
+
+void MultiplayerAPIExtension::_bind_methods() {
+ GDVIRTUAL_BIND(_poll);
+ GDVIRTUAL_BIND(_set_multiplayer_peer, "multiplayer_peer");
+ GDVIRTUAL_BIND(_get_multiplayer_peer);
+ GDVIRTUAL_BIND(_get_unique_id);
+ GDVIRTUAL_BIND(_get_peer_ids);
+ GDVIRTUAL_BIND(_rpc, "peer", "object", "method", "args");
+ GDVIRTUAL_BIND(_get_remote_sender_id);
+ GDVIRTUAL_BIND(_object_configuration_add, "object", "configuration");
+ GDVIRTUAL_BIND(_object_configuration_remove, "object", "configuration");
+}
diff --git a/scene/main/multiplayer_api.h b/scene/main/multiplayer_api.h
new file mode 100644
index 0000000000..c1d90d651e
--- /dev/null
+++ b/scene/main/multiplayer_api.h
@@ -0,0 +1,115 @@
+/*************************************************************************/
+/* multiplayer_api.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef MULTIPLAYER_API_H
+#define MULTIPLAYER_API_H
+
+#include "core/object/ref_counted.h"
+#include "scene/main/multiplayer_peer.h"
+
+class MultiplayerAPI : public RefCounted {
+ GDCLASS(MultiplayerAPI, RefCounted);
+
+private:
+ static StringName default_interface;
+
+protected:
+ static void _bind_methods();
+ Error _rpc_bind(int p_peer, Object *p_obj, const StringName &p_method, Array args = Array());
+
+public:
+ enum RPCMode {
+ RPC_MODE_DISABLED, // No rpc for this method, calls to this will be blocked (default)
+ RPC_MODE_ANY_PEER, // Any peer can call this RPC
+ RPC_MODE_AUTHORITY, // Only the node's multiplayer authority (server by default) can call this RPC
+ };
+
+ static Ref<MultiplayerAPI> create_default_interface();
+ static void set_default_interface(const StringName &p_interface);
+ static StringName get_default_interface();
+
+ static Error encode_and_compress_variant(const Variant &p_variant, uint8_t *p_buffer, int &r_len, bool p_allow_object_decoding);
+ static Error decode_and_decompress_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int *r_len, bool p_allow_object_decoding);
+ static Error encode_and_compress_variants(const Variant **p_variants, int p_count, uint8_t *p_buffer, int &r_len, bool *r_raw = nullptr, bool p_allow_object_decoding = false);
+ static Error decode_and_decompress_variants(Vector<Variant> &r_variants, const uint8_t *p_buffer, int p_len, int &r_len, bool p_raw = false, bool p_allow_object_decoding = false);
+
+ virtual Error poll() = 0;
+ virtual void set_multiplayer_peer(const Ref<MultiplayerPeer> &p_peer) = 0;
+ virtual Ref<MultiplayerPeer> get_multiplayer_peer() = 0;
+ virtual int get_unique_id() = 0;
+ virtual Vector<int> get_peer_ids() = 0;
+
+ virtual Error rpcp(Object *p_obj, int p_peer_id, const StringName &p_method, const Variant **p_arg, int p_argcount) = 0;
+ virtual int get_remote_sender_id() = 0;
+
+ virtual Error object_configuration_add(Object *p_object, Variant p_config) = 0;
+ virtual Error object_configuration_remove(Object *p_object, Variant p_config) = 0;
+
+ bool has_multiplayer_peer() { return get_multiplayer_peer().is_valid(); }
+ bool is_server() { return get_unique_id() == MultiplayerPeer::TARGET_PEER_SERVER; }
+
+ MultiplayerAPI() {}
+ virtual ~MultiplayerAPI() {}
+};
+
+VARIANT_ENUM_CAST(MultiplayerAPI::RPCMode);
+
+class MultiplayerAPIExtension : public MultiplayerAPI {
+ GDCLASS(MultiplayerAPIExtension, MultiplayerAPI);
+
+protected:
+ static void _bind_methods();
+
+public:
+ virtual Error poll() override;
+ virtual void set_multiplayer_peer(const Ref<MultiplayerPeer> &p_peer) override;
+ virtual Ref<MultiplayerPeer> get_multiplayer_peer() override;
+ virtual int get_unique_id() override;
+ virtual Vector<int> get_peer_ids() override;
+
+ virtual Error rpcp(Object *p_obj, int p_peer_id, const StringName &p_method, const Variant **p_arg, int p_argcount) override;
+ virtual int get_remote_sender_id() override;
+
+ virtual Error object_configuration_add(Object *p_object, Variant p_config) override;
+ virtual Error object_configuration_remove(Object *p_object, Variant p_config) override;
+
+ // Extensions
+ GDVIRTUAL0R(int, _poll);
+ GDVIRTUAL1(_set_multiplayer_peer, Ref<MultiplayerPeer>);
+ GDVIRTUAL0R(Ref<MultiplayerPeer>, _get_multiplayer_peer);
+ GDVIRTUAL0RC(int, _get_unique_id);
+ GDVIRTUAL0RC(PackedInt32Array, _get_peer_ids);
+ GDVIRTUAL4R(int, _rpc, int, Object *, StringName, Array);
+ GDVIRTUAL0RC(int, _get_remote_sender_id);
+ GDVIRTUAL2R(int, _object_configuration_add, Object *, Variant);
+ GDVIRTUAL2R(int, _object_configuration_remove, Object *, Variant);
+};
+
+#endif // MULTIPLAYER_API_H
diff --git a/scene/main/multiplayer_peer.cpp b/scene/main/multiplayer_peer.cpp
new file mode 100644
index 0000000000..462dc1babb
--- /dev/null
+++ b/scene/main/multiplayer_peer.cpp
@@ -0,0 +1,223 @@
+/*************************************************************************/
+/* multiplayer_peer.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "multiplayer_peer.h"
+
+#include "core/os/os.h"
+
+uint32_t MultiplayerPeer::generate_unique_id() const {
+ uint32_t hash = 0;
+
+ while (hash == 0 || hash == 1) {
+ hash = hash_murmur3_one_32(
+ (uint32_t)OS::get_singleton()->get_ticks_usec());
+ hash = hash_murmur3_one_32(
+ (uint32_t)OS::get_singleton()->get_unix_time(), hash);
+ hash = hash_murmur3_one_32(
+ (uint32_t)OS::get_singleton()->get_user_data_dir().hash64(), hash);
+ hash = hash_murmur3_one_32(
+ (uint32_t)((uint64_t)this), hash); // Rely on ASLR heap
+ hash = hash_murmur3_one_32(
+ (uint32_t)((uint64_t)&hash), hash); // Rely on ASLR stack
+
+ hash = hash_fmix32(hash);
+ hash = hash & 0x7FFFFFFF; // Make it compatible with unsigned, since negative ID is used for exclusion
+ }
+
+ return hash;
+}
+
+void MultiplayerPeer::set_transfer_channel(int p_channel) {
+ transfer_channel = p_channel;
+}
+
+int MultiplayerPeer::get_transfer_channel() const {
+ return transfer_channel;
+}
+
+void MultiplayerPeer::set_transfer_mode(TransferMode p_mode) {
+ transfer_mode = p_mode;
+}
+
+MultiplayerPeer::TransferMode MultiplayerPeer::get_transfer_mode() const {
+ return transfer_mode;
+}
+
+void MultiplayerPeer::set_refuse_new_connections(bool p_enable) {
+ refuse_connections = p_enable;
+}
+
+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);
+ ClassDB::bind_method(D_METHOD("set_transfer_mode", "mode"), &MultiplayerPeer::set_transfer_mode);
+ ClassDB::bind_method(D_METHOD("get_transfer_mode"), &MultiplayerPeer::get_transfer_mode);
+ 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("get_connection_status"), &MultiplayerPeer::get_connection_status);
+ ClassDB::bind_method(D_METHOD("get_unique_id"), &MultiplayerPeer::get_unique_id);
+ ClassDB::bind_method(D_METHOD("generate_unique_id"), &MultiplayerPeer::generate_unique_id);
+
+ 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");
+
+ BIND_ENUM_CONSTANT(CONNECTION_DISCONNECTED);
+ BIND_ENUM_CONSTANT(CONNECTION_CONNECTING);
+ BIND_ENUM_CONSTANT(CONNECTION_CONNECTED);
+
+ BIND_CONSTANT(TARGET_PEER_BROADCAST);
+ BIND_CONSTANT(TARGET_PEER_SERVER);
+
+ BIND_ENUM_CONSTANT(TRANSFER_MODE_UNRELIABLE);
+ BIND_ENUM_CONSTANT(TRANSFER_MODE_UNRELIABLE_ORDERED);
+ BIND_ENUM_CONSTANT(TRANSFER_MODE_RELIABLE);
+
+ 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"));
+}
+
+/*************/
+
+Error MultiplayerPeerExtension::get_packet(const uint8_t **r_buffer, int &r_buffer_size) {
+ Error err;
+ if (GDVIRTUAL_CALL(_get_packet, r_buffer, &r_buffer_size, err)) {
+ return err;
+ }
+ if (GDVIRTUAL_IS_OVERRIDDEN(_get_packet_script)) {
+ if (!GDVIRTUAL_CALL(_get_packet_script, script_buffer)) {
+ return FAILED;
+ }
+
+ if (script_buffer.size() == 0) {
+ return Error::ERR_UNAVAILABLE;
+ }
+
+ *r_buffer = script_buffer.ptr();
+ r_buffer_size = script_buffer.size();
+
+ return Error::OK;
+ }
+ WARN_PRINT_ONCE("MultiplayerPeerExtension::_get_packet_native is unimplemented!");
+ return FAILED;
+}
+
+Error MultiplayerPeerExtension::put_packet(const uint8_t *p_buffer, int p_buffer_size) {
+ Error err;
+ if (GDVIRTUAL_CALL(_put_packet, p_buffer, p_buffer_size, err)) {
+ return err;
+ }
+ if (GDVIRTUAL_IS_OVERRIDDEN(_put_packet_script)) {
+ PackedByteArray a;
+ a.resize(p_buffer_size);
+ memcpy(a.ptrw(), p_buffer, p_buffer_size);
+
+ if (!GDVIRTUAL_CALL(_put_packet_script, a, err)) {
+ return FAILED;
+ }
+ return (Error)err;
+ }
+ WARN_PRINT_ONCE("MultiplayerPeerExtension::_put_packet_native is unimplemented!");
+ return FAILED;
+}
+
+void MultiplayerPeerExtension::set_refuse_new_connections(bool p_enable) {
+ if (GDVIRTUAL_CALL(_set_refuse_new_connections, p_enable)) {
+ return;
+ }
+ MultiplayerPeer::set_refuse_new_connections(p_enable);
+}
+
+bool MultiplayerPeerExtension::is_refusing_new_connections() const {
+ bool refusing;
+ if (GDVIRTUAL_CALL(_is_refusing_new_connections, refusing)) {
+ return refusing;
+ }
+ return MultiplayerPeer::is_refusing_new_connections();
+}
+
+bool MultiplayerPeerExtension::is_server_relay_supported() const {
+ bool can_relay;
+ if (GDVIRTUAL_CALL(_is_server_relay_supported, can_relay)) {
+ return can_relay;
+ }
+ return MultiplayerPeer::is_server_relay_supported();
+}
+
+void MultiplayerPeerExtension::_bind_methods() {
+ GDVIRTUAL_BIND(_get_packet, "r_buffer", "r_buffer_size");
+ GDVIRTUAL_BIND(_put_packet, "p_buffer", "p_buffer_size");
+ GDVIRTUAL_BIND(_get_available_packet_count);
+ GDVIRTUAL_BIND(_get_max_packet_size);
+
+ GDVIRTUAL_BIND(_get_packet_script)
+ GDVIRTUAL_BIND(_put_packet_script, "p_buffer");
+
+ GDVIRTUAL_BIND(_set_transfer_channel, "p_channel");
+ GDVIRTUAL_BIND(_get_transfer_channel);
+
+ GDVIRTUAL_BIND(_set_transfer_mode, "p_mode");
+ GDVIRTUAL_BIND(_get_transfer_mode);
+
+ GDVIRTUAL_BIND(_set_target_peer, "p_peer");
+
+ GDVIRTUAL_BIND(_get_packet_peer);
+ GDVIRTUAL_BIND(_is_server);
+ GDVIRTUAL_BIND(_poll);
+ 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
new file mode 100644
index 0000000000..63ce66871e
--- /dev/null
+++ b/scene/main/multiplayer_peer.h
@@ -0,0 +1,146 @@
+/*************************************************************************/
+/* multiplayer_peer.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef MULTIPLAYER_PEER_H
+#define MULTIPLAYER_PEER_H
+
+#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"
+
+class MultiplayerPeer : public PacketPeer {
+ GDCLASS(MultiplayerPeer, PacketPeer);
+
+public:
+ enum TransferMode {
+ TRANSFER_MODE_UNRELIABLE,
+ TRANSFER_MODE_UNRELIABLE_ORDERED,
+ TRANSFER_MODE_RELIABLE
+ };
+
+protected:
+ static void _bind_methods();
+
+private:
+ int transfer_channel = 0;
+ TransferMode transfer_mode = TRANSFER_MODE_RELIABLE;
+ bool refuse_connections = false;
+
+public:
+ enum {
+ TARGET_PEER_BROADCAST = 0,
+ TARGET_PEER_SERVER = 1
+ };
+
+ enum ConnectionStatus {
+ CONNECTION_DISCONNECTED,
+ CONNECTION_CONNECTING,
+ CONNECTION_CONNECTED,
+ };
+
+ virtual void set_transfer_channel(int p_channel);
+ virtual int get_transfer_channel() const;
+ virtual void set_transfer_mode(TransferMode p_mode);
+ 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 bool is_server() const = 0;
+
+ virtual void poll() = 0;
+
+ virtual int get_unique_id() const = 0;
+
+ virtual ConnectionStatus get_connection_status() const = 0;
+
+ uint32_t generate_unique_id() const;
+
+ MultiplayerPeer() {}
+};
+
+VARIANT_ENUM_CAST(MultiplayerPeer::ConnectionStatus);
+VARIANT_ENUM_CAST(MultiplayerPeer::TransferMode);
+
+class MultiplayerPeerExtension : public MultiplayerPeer {
+ GDCLASS(MultiplayerPeerExtension, MultiplayerPeer);
+
+protected:
+ static void _bind_methods();
+
+ PackedByteArray script_buffer;
+
+public:
+ /* PacketPeer extension */
+ virtual Error get_packet(const uint8_t **r_buffer, int &r_buffer_size) override; ///< buffer is GONE after next get_packet
+ GDVIRTUAL2R(Error, _get_packet, GDNativeConstPtr<const uint8_t *>, GDNativePtr<int>);
+ GDVIRTUAL0R(PackedByteArray, _get_packet_script); // For GDScript.
+
+ 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.
+
+ EXBIND0RC(int, get_available_packet_count);
+ EXBIND0RC(int, get_max_packet_size);
+
+ /* MultiplayerPeer extension */
+ virtual void set_refuse_new_connections(bool p_enable) override;
+ GDVIRTUAL1(_set_refuse_new_connections, bool); // Optional.
+
+ 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);
+ 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 b4701637a4..25b38eb15b 100644
--- a/scene/main/node.cpp
+++ b/scene/main/node.cpp
@@ -33,12 +33,12 @@
#include "core/config/project_settings.h"
#include "core/core_string_names.h"
#include "core/io/resource_loader.h"
-#include "core/multiplayer/multiplayer_api.h"
#include "core/object/message_queue.h"
#include "core/string/print_string.h"
#include "instance_placeholder.h"
#include "scene/animation/tween.h"
#include "scene/debugger/scene_debugger.h"
+#include "scene/main/multiplayer_api.h"
#include "scene/resources/packed_scene.h"
#include "scene/scene_string_names.h"
#include "viewport.h"
@@ -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) {
@@ -582,35 +576,30 @@ bool Node::is_multiplayer_authority() const {
/***** RPC CONFIG ********/
-uint16_t Node::rpc_config(const StringName &p_method, Multiplayer::RPCMode p_rpc_mode, bool p_call_local, Multiplayer::TransferMode p_transfer_mode, int p_channel) {
- for (int i = 0; i < data.rpc_methods.size(); i++) {
- if (data.rpc_methods[i].name == p_method) {
- Multiplayer::RPCConfig &nd = data.rpc_methods.write[i];
- nd.rpc_mode = p_rpc_mode;
- nd.transfer_mode = p_transfer_mode;
- nd.call_local = p_call_local;
- nd.channel = p_channel;
- return i | (1 << 15);
- }
+void Node::rpc_config(const StringName &p_method, const Variant &p_config) {
+ if (data.rpc_config.get_type() != Variant::DICTIONARY) {
+ data.rpc_config = Dictionary();
+ }
+ Dictionary node_config = data.rpc_config;
+ if (p_config.get_type() == Variant::NIL) {
+ node_config.erase(p_method);
+ } else {
+ ERR_FAIL_COND(p_config.get_type() != Variant::DICTIONARY);
+ node_config[p_method] = p_config;
}
- // New method
- Multiplayer::RPCConfig nd;
- nd.name = p_method;
- nd.rpc_mode = p_rpc_mode;
- nd.transfer_mode = p_transfer_mode;
- nd.channel = p_channel;
- nd.call_local = p_call_local;
- data.rpc_methods.push_back(nd);
- return ((uint16_t)data.rpc_methods.size() - 1) | (1 << 15);
+}
+
+const Variant Node::get_node_rpc_config() const {
+ return data.rpc_config;
}
/***** RPC FUNCTIONS ********/
-void Node::_rpc_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
+Error Node::_rpc_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
if (p_argcount < 1) {
r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
r_error.argument = 1;
- return;
+ return ERR_INVALID_PARAMETER;
}
Variant::Type type = p_args[0]->get_type();
@@ -618,28 +607,28 @@ void Node::_rpc_bind(const Variant **p_args, int p_argcount, Callable::CallError
r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
r_error.argument = 0;
r_error.expected = Variant::STRING_NAME;
- return;
+ return ERR_INVALID_PARAMETER;
}
StringName method = (*p_args[0]).operator StringName();
- rpcp(0, method, &p_args[1], p_argcount - 1);
-
+ Error err = rpcp(0, method, &p_args[1], p_argcount - 1);
r_error.error = Callable::CallError::CALL_OK;
+ return err;
}
-void Node::_rpc_id_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
+Error Node::_rpc_id_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
if (p_argcount < 2) {
r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
r_error.argument = 2;
- return;
+ return ERR_INVALID_PARAMETER;
}
if (p_args[0]->get_type() != Variant::INT) {
r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
r_error.argument = 0;
r_error.expected = Variant::INT;
- return;
+ return ERR_INVALID_PARAMETER;
}
Variant::Type type = p_args[1]->get_type();
@@ -647,20 +636,20 @@ void Node::_rpc_id_bind(const Variant **p_args, int p_argcount, Callable::CallEr
r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
r_error.argument = 1;
r_error.expected = Variant::STRING_NAME;
- return;
+ return ERR_INVALID_PARAMETER;
}
int peer_id = *p_args[0];
StringName method = (*p_args[1]).operator StringName();
- rpcp(peer_id, method, &p_args[2], p_argcount - 2);
-
+ Error err = rpcp(peer_id, method, &p_args[2], p_argcount - 2);
r_error.error = Callable::CallError::CALL_OK;
+ return err;
}
-void Node::rpcp(int p_peer_id, const StringName &p_method, const Variant **p_arg, int p_argcount) {
- ERR_FAIL_COND(!is_inside_tree());
- get_multiplayer()->rpcp(this, p_peer_id, p_method, p_arg, p_argcount);
+Error Node::rpcp(int p_peer_id, const StringName &p_method, const Variant **p_arg, int p_argcount) {
+ ERR_FAIL_COND_V(!is_inside_tree(), ERR_UNCONFIGURED);
+ return get_multiplayer()->rpcp(this, p_peer_id, p_method, p_arg, p_argcount);
}
Ref<MultiplayerAPI> Node::get_multiplayer() const {
@@ -670,10 +659,6 @@ Ref<MultiplayerAPI> Node::get_multiplayer() const {
return get_tree()->get_multiplayer(get_path());
}
-Vector<Multiplayer::RPCConfig> Node::get_node_rpc_methods() const {
- return data.rpc_methods;
-}
-
//////////// end of rpc
bool Node::can_process_notification(int p_what) const {
@@ -957,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 */
@@ -1027,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
@@ -1120,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;
@@ -1139,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
@@ -1148,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) {
@@ -1162,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!
@@ -1175,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);
}
@@ -1187,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;
}
}
@@ -1225,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();
@@ -1357,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));
}
}
@@ -1425,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();
}
}
@@ -1480,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);
@@ -1507,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;
}
@@ -1787,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) {
@@ -1918,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() {
@@ -1930,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;
}
@@ -2182,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);
}
}
@@ -2404,7 +2352,7 @@ void Node::_duplicate_signals(const Node *p_original, Node *p_copy) const {
if (copy && copytarget) {
const Callable copy_callable = Callable(copytarget, E.callable.get_method());
if (!copy->is_connected(E.signal.get_name(), copy_callable)) {
- copy->connect(E.signal.get_name(), copy_callable, E.binds, E.flags);
+ copy->connect(E.signal.get_name(), copy_callable, E.flags);
}
}
}
@@ -2452,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()) {
@@ -2490,7 +2438,7 @@ void Node::_replace_connections_target(Node *p_new_target) {
c.signal.get_object()->disconnect(c.signal.get_name(), Callable(this, c.callable.get_method()));
bool valid = p_new_target->has_method(c.callable.get_method()) || Ref<Script>(p_new_target->get_script()).is_null() || Ref<Script>(p_new_target->get_script())->has_method(c.callable.get_method());
ERR_CONTINUE_MSG(!valid, vformat("Attempt to connect signal '%s.%s' to nonexistent method '%s.%s'.", c.signal.get_object()->get_class(), c.signal.get_name(), c.callable.get_object()->get_class(), c.callable.get_method()));
- c.signal.get_object()->connect(c.signal.get_name(), Callable(p_new_target, c.callable.get_method()), c.binds, c.flags);
+ c.signal.get_object()->connect(c.signal.get_name(), Callable(p_new_target, c.callable.get_method()), c.flags);
}
}
}
@@ -2691,21 +2639,19 @@ 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();
+ PackedStringArray warnings = get_configuration_warnings();
String all_warnings = String();
for (int i = 0; i < warnings.size(); i++) {
if (i > 0) {
@@ -2713,7 +2659,7 @@ String Node::get_configuration_warnings_as_string() const {
}
// 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;
}
@@ -2795,11 +2741,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));
@@ -2822,12 +2768,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);
@@ -2888,7 +2832,7 @@ void Node::_bind_methods() {
ClassDB::bind_method(D_METHOD("is_multiplayer_authority"), &Node::is_multiplayer_authority);
ClassDB::bind_method(D_METHOD("get_multiplayer"), &Node::get_multiplayer);
- ClassDB::bind_method(D_METHOD("rpc_config", "method", "rpc_mode", "call_local", "transfer_mode", "channel"), &Node::rpc_config, DEFVAL(false), DEFVAL(Multiplayer::TRANSFER_MODE_RELIABLE), DEFVAL(0));
+ ClassDB::bind_method(D_METHOD("rpc_config", "method", "config"), &Node::rpc_config);
ClassDB::bind_method(D_METHOD("set_editor_description", "editor_description"), &Node::set_editor_description);
ClassDB::bind_method(D_METHOD("get_editor_description"), &Node::get_editor_description);
@@ -2931,7 +2875,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);
diff --git a/scene/main/node.h b/scene/main/node.h
index 3c4727f11c..c8c8c395ce 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;
@@ -127,7 +128,7 @@ private:
Node *process_owner = nullptr;
int multiplayer_authority = 1; // Server by default.
- Vector<Multiplayer::RPCConfig> rpc_methods;
+ Variant rpc_config;
// Variables used to properly sort the node when processing, ignored otherwise.
// TODO: Should move all the stuff below to bits.
@@ -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;
- void _rpc_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error);
- void _rpc_id_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error);
+ 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,6 +458,7 @@ public:
#ifdef TOOLS_ENABLED
String validate_child_name(Node *p_child);
#endif
+ static String adjust_name_casing(const String &p_name);
void queue_delete();
@@ -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();
@@ -491,30 +491,16 @@ public:
int get_multiplayer_authority() const;
bool is_multiplayer_authority() const;
- uint16_t rpc_config(const StringName &p_method, Multiplayer::RPCMode p_rpc_mode, bool p_call_local = false, Multiplayer::TransferMode p_transfer_mode = Multiplayer::TRANSFER_MODE_RELIABLE, int p_channel = 0); // config a local method for RPC
- Vector<Multiplayer::RPCConfig> get_node_rpc_methods() const;
+ void rpc_config(const StringName &p_method, const Variant &p_config); // config a local method for RPC
+ const Variant get_node_rpc_config() const;
template <typename... VarArgs>
- void rpc(const StringName &p_method, VarArgs... p_args) {
- Variant args[sizeof...(p_args) + 1] = { p_args..., Variant() }; // +1 makes sure zero sized arrays are also supported.
- const Variant *argptrs[sizeof...(p_args) + 1];
- for (uint32_t i = 0; i < sizeof...(p_args); i++) {
- argptrs[i] = &args[i];
- }
- rpcp(0, p_method, sizeof...(p_args) == 0 ? nullptr : (const Variant **)argptrs, sizeof...(p_args));
- }
+ Error rpc(const StringName &p_method, VarArgs... p_args);
template <typename... VarArgs>
- void rpc_id(int p_peer_id, const StringName &p_method, VarArgs... p_args) {
- Variant args[sizeof...(p_args) + 1] = { p_args..., Variant() }; // +1 makes sure zero sized arrays are also supported.
- const Variant *argptrs[sizeof...(p_args) + 1];
- for (uint32_t i = 0; i < sizeof...(p_args); i++) {
- argptrs[i] = &args[i];
- }
- rpcp(p_peer_id, p_method, sizeof...(p_args) == 0 ? nullptr : (const Variant **)argptrs, sizeof...(p_args));
- }
+ Error rpc_id(int p_peer_id, const StringName &p_method, VarArgs... p_args);
- void rpcp(int p_peer_id, const StringName &p_method, const Variant **p_arg, int p_argcount);
+ Error rpcp(int p_peer_id, const StringName &p_method, const Variant **p_arg, int p_argcount);
Ref<MultiplayerAPI> get_multiplayer() const;
@@ -526,4 +512,26 @@ VARIANT_ENUM_CAST(Node::DuplicateFlags);
typedef HashSet<Node *, Node::Comparator> NodeSet;
-#endif
+// Template definitions must be in the header so they are always fully initialized before their usage.
+// See this StackOverflow question for more information: https://stackoverflow.com/questions/495021/why-can-templates-only-be-implemented-in-the-header-file
+
+template <typename... VarArgs>
+Error Node::rpc(const StringName &p_method, VarArgs... p_args) {
+ return rpc_id(0, p_method, p_args...);
+}
+
+template <typename... VarArgs>
+Error Node::rpc_id(int p_peer_id, const StringName &p_method, VarArgs... p_args) {
+ Variant args[sizeof...(p_args) + 1] = { p_args..., Variant() }; // +1 makes sure zero sized arrays are also supported.
+ const Variant *argptrs[sizeof...(p_args) + 1];
+ for (uint32_t i = 0; i < sizeof...(p_args); i++) {
+ argptrs[i] = &args[i];
+ }
+ 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/resource_preloader.cpp b/scene/main/resource_preloader.cpp
index 5512d0a84e..a9b0285723 100644
--- a/scene/main/resource_preloader.cpp
+++ b/scene/main/resource_preloader.cpp
@@ -138,7 +138,7 @@ void ResourcePreloader::get_resource_list(List<StringName> *p_list) {
}
void ResourcePreloader::_bind_methods() {
- ClassDB::bind_method(D_METHOD("_set_resources"), &ResourcePreloader::_set_resources);
+ ClassDB::bind_method(D_METHOD("_set_resources", "resources"), &ResourcePreloader::_set_resources);
ClassDB::bind_method(D_METHOD("_get_resources"), &ResourcePreloader::_get_resources);
ClassDB::bind_method(D_METHOD("add_resource", "name", "resource"), &ResourcePreloader::add_resource);
diff --git a/scene/main/scene_tree.cpp b/scene/main/scene_tree.cpp
index 66482f65dc..5ff9a29792 100644
--- a/scene/main/scene_tree.cpp
+++ b/scene/main/scene_tree.cpp
@@ -37,7 +37,6 @@
#include "core/io/image_loader.h"
#include "core/io/marshalls.h"
#include "core/io/resource_loader.h"
-#include "core/multiplayer/multiplayer_api.h"
#include "core/object/message_queue.h"
#include "core/os/keyboard.h"
#include "core/os/os.h"
@@ -45,7 +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);
}
}
@@ -198,15 +208,15 @@ void SceneTree::_update_group_order(Group &g, bool p_use_priority) {
return;
}
- Node **nodes = g.nodes.ptrw();
- int node_count = g.nodes.size();
+ Node **gr_nodes = g.nodes.ptrw();
+ int gr_node_count = g.nodes.size();
if (p_use_priority) {
SortArray<Node *, Node::ComparatorWithPriority> node_sort;
- node_sort.sort(nodes, node_count);
+ node_sort.sort(gr_nodes, gr_node_count);
} else {
SortArray<Node *, Node::Comparator> node_sort;
- node_sort.sort(nodes, node_count);
+ node_sort.sort(gr_nodes, gr_node_count);
}
g.changed = false;
}
@@ -244,36 +254,36 @@ void SceneTree::call_group_flagsp(uint32_t p_call_flags, const StringName &p_gro
_update_group_order(g);
Vector<Node *> nodes_copy = g.nodes;
- Node **nodes = nodes_copy.ptrw();
- int node_count = nodes_copy.size();
+ Node **gr_nodes = nodes_copy.ptrw();
+ int gr_node_count = nodes_copy.size();
call_lock++;
if (p_call_flags & GROUP_CALL_REVERSE) {
- for (int i = node_count - 1; i >= 0; i--) {
- if (call_lock && call_skip.has(nodes[i])) {
+ for (int i = gr_node_count - 1; i >= 0; i--) {
+ if (call_lock && call_skip.has(gr_nodes[i])) {
continue;
}
if (!(p_call_flags & GROUP_CALL_DEFERRED)) {
Callable::CallError ce;
- nodes[i]->callp(p_function, p_args, p_argcount, ce);
+ gr_nodes[i]->callp(p_function, p_args, p_argcount, ce);
} else {
- MessageQueue::get_singleton()->push_callp(nodes[i], p_function, p_args, p_argcount);
+ MessageQueue::get_singleton()->push_callp(gr_nodes[i], p_function, p_args, p_argcount);
}
}
} else {
- for (int i = 0; i < node_count; i++) {
- if (call_lock && call_skip.has(nodes[i])) {
+ for (int i = 0; i < gr_node_count; i++) {
+ if (call_lock && call_skip.has(gr_nodes[i])) {
continue;
}
if (!(p_call_flags & GROUP_CALL_DEFERRED)) {
Callable::CallError ce;
- nodes[i]->callp(p_function, p_args, p_argcount, ce);
+ gr_nodes[i]->callp(p_function, p_args, p_argcount, ce);
} else {
- MessageQueue::get_singleton()->push_callp(nodes[i], p_function, p_args, p_argcount);
+ MessageQueue::get_singleton()->push_callp(gr_nodes[i], p_function, p_args, p_argcount);
}
}
}
@@ -297,34 +307,34 @@ void SceneTree::notify_group_flags(uint32_t p_call_flags, const StringName &p_gr
_update_group_order(g);
Vector<Node *> nodes_copy = g.nodes;
- Node **nodes = nodes_copy.ptrw();
- int node_count = nodes_copy.size();
+ Node **gr_nodes = nodes_copy.ptrw();
+ int gr_node_count = nodes_copy.size();
call_lock++;
if (p_call_flags & GROUP_CALL_REVERSE) {
- for (int i = node_count - 1; i >= 0; i--) {
- if (call_lock && call_skip.has(nodes[i])) {
+ for (int i = gr_node_count - 1; i >= 0; i--) {
+ if (call_lock && call_skip.has(gr_nodes[i])) {
continue;
}
if (!(p_call_flags & GROUP_CALL_DEFERRED)) {
- nodes[i]->notification(p_notification);
+ gr_nodes[i]->notification(p_notification);
} else {
- MessageQueue::get_singleton()->push_notification(nodes[i], p_notification);
+ MessageQueue::get_singleton()->push_notification(gr_nodes[i], p_notification);
}
}
} else {
- for (int i = 0; i < node_count; i++) {
- if (call_lock && call_skip.has(nodes[i])) {
+ for (int i = 0; i < gr_node_count; i++) {
+ if (call_lock && call_skip.has(gr_nodes[i])) {
continue;
}
if (!(p_call_flags & GROUP_CALL_DEFERRED)) {
- nodes[i]->notification(p_notification);
+ gr_nodes[i]->notification(p_notification);
} else {
- MessageQueue::get_singleton()->push_notification(nodes[i], p_notification);
+ MessageQueue::get_singleton()->push_notification(gr_nodes[i], p_notification);
}
}
}
@@ -348,34 +358,34 @@ void SceneTree::set_group_flags(uint32_t p_call_flags, const StringName &p_group
_update_group_order(g);
Vector<Node *> nodes_copy = g.nodes;
- Node **nodes = nodes_copy.ptrw();
- int node_count = nodes_copy.size();
+ Node **gr_nodes = nodes_copy.ptrw();
+ int gr_node_count = nodes_copy.size();
call_lock++;
if (p_call_flags & GROUP_CALL_REVERSE) {
- for (int i = node_count - 1; i >= 0; i--) {
- if (call_lock && call_skip.has(nodes[i])) {
+ for (int i = gr_node_count - 1; i >= 0; i--) {
+ if (call_lock && call_skip.has(gr_nodes[i])) {
continue;
}
if (!(p_call_flags & GROUP_CALL_DEFERRED)) {
- nodes[i]->set(p_name, p_value);
+ gr_nodes[i]->set(p_name, p_value);
} else {
- MessageQueue::get_singleton()->push_set(nodes[i], p_name, p_value);
+ MessageQueue::get_singleton()->push_set(gr_nodes[i], p_name, p_value);
}
}
} else {
- for (int i = 0; i < node_count; i++) {
- if (call_lock && call_skip.has(nodes[i])) {
+ for (int i = 0; i < gr_node_count; i++) {
+ if (call_lock && call_skip.has(gr_nodes[i])) {
continue;
}
if (!(p_call_flags & GROUP_CALL_DEFERRED)) {
- nodes[i]->set(p_name, p_value);
+ gr_nodes[i]->set(p_name, p_value);
} else {
- MessageQueue::get_singleton()->push_set(nodes[i], p_name, p_value);
+ MessageQueue::get_singleton()->push_set(gr_nodes[i], p_name, p_value);
}
}
}
@@ -419,6 +429,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 +473,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);
@@ -529,7 +511,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 +723,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 +740,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,13 +847,13 @@ void SceneTree::_notify_group_pause(const StringName &p_group, int p_notificatio
//performance is not lost because only if something is added/removed the vector is copied.
Vector<Node *> nodes_copy = g.nodes;
- int node_count = nodes_copy.size();
- Node **nodes = nodes_copy.ptrw();
+ int gr_node_count = nodes_copy.size();
+ Node **gr_nodes = nodes_copy.ptrw();
call_lock++;
- for (int i = 0; i < node_count; i++) {
- Node *n = nodes[i];
+ for (int i = 0; i < gr_node_count; i++) {
+ Node *n = gr_nodes[i];
if (call_lock && call_skip.has(n)) {
continue;
}
@@ -902,7 +866,7 @@ void SceneTree::_notify_group_pause(const StringName &p_group, int p_notificatio
}
n->notification(p_notification);
- //ERR_FAIL_COND(node_count != g.nodes.size());
+ //ERR_FAIL_COND(gr_node_count != g.nodes.size());
}
call_lock--;
@@ -927,17 +891,19 @@ void SceneTree::_call_input_pause(const StringName &p_group, CallInputType p_cal
//performance is not lost because only if something is added/removed the vector is copied.
Vector<Node *> nodes_copy = g.nodes;
- int node_count = nodes_copy.size();
- Node **nodes = nodes_copy.ptrw();
+ int gr_node_count = nodes_copy.size();
+ Node **gr_nodes = nodes_copy.ptrw();
call_lock++;
- for (int i = node_count - 1; i >= 0; i--) {
+ Vector<ObjectID> no_context_node_ids; // Nodes may be deleted due to this shortcut input.
+
+ for (int i = gr_node_count - 1; i >= 0; i--) {
if (p_viewport->is_input_handled()) {
break;
}
- Node *n = nodes[i];
+ Node *n = gr_nodes[i];
if (call_lock && call_skip.has(n)) {
continue;
}
@@ -950,9 +916,22 @@ void SceneTree::_call_input_pause(const StringName &p_group, CallInputType p_cal
case CALL_INPUT_TYPE_INPUT:
n->_call_input(p_input);
break;
- case CALL_INPUT_TYPE_SHORTCUT_INPUT:
+ case CALL_INPUT_TYPE_SHORTCUT_INPUT: {
+ const Control *c = Object::cast_to<Control>(n);
+ if (c) {
+ // If calling shortcut input on a control, ensure it respects the shortcut context.
+ // Shortcut context (based on focus) only makes sense for controls (UI), so don't need to worry about it for nodes
+ if (c->get_shortcut_context() == nullptr) {
+ no_context_node_ids.append(n->get_instance_id());
+ continue;
+ }
+ if (!c->is_focus_owner_in_shortcut_context()) {
+ continue;
+ }
+ }
n->_call_shortcut_input(p_input);
break;
+ }
case CALL_INPUT_TYPE_UNHANDLED_INPUT:
n->_call_unhandled_input(p_input);
break;
@@ -962,6 +941,13 @@ void SceneTree::_call_input_pause(const StringName &p_group, CallInputType p_cal
}
}
+ for (const ObjectID &id : no_context_node_ids) {
+ Node *n = Object::cast_to<Node>(ObjectDB::get_instance(id));
+ if (n) {
+ n->_call_shortcut_input(p_input);
+ }
+ }
+
call_lock--;
if (call_lock == 0) {
call_skip.clear();
@@ -1000,8 +986,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 +1108,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 +1135,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 +1143,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 +1160,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;
@@ -1213,19 +1202,17 @@ void SceneTree::set_multiplayer(Ref<MultiplayerAPI> p_multiplayer, const NodePat
if (p_root_path.is_empty()) {
ERR_FAIL_COND(!p_multiplayer.is_valid());
if (multiplayer.is_valid()) {
- multiplayer->set_root_path(NodePath());
+ multiplayer->object_configuration_remove(nullptr, NodePath("/" + root->get_name()));
}
multiplayer = p_multiplayer;
- multiplayer->set_root_path("/" + root->get_name());
+ multiplayer->object_configuration_add(nullptr, NodePath("/" + root->get_name()));
} else {
+ if (custom_multiplayers.has(p_root_path)) {
+ custom_multiplayers[p_root_path]->object_configuration_remove(nullptr, p_root_path);
+ }
if (p_multiplayer.is_valid()) {
custom_multiplayers[p_root_path] = p_multiplayer;
- p_multiplayer->set_root_path(p_root_path);
- } else {
- if (custom_multiplayers.has(p_root_path)) {
- custom_multiplayers[p_root_path]->set_root_path(NodePath());
- custom_multiplayers.erase(p_root_path);
- }
+ p_multiplayer->object_configuration_add(nullptr, p_root_path);
}
}
}
@@ -1260,7 +1247,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);
@@ -1297,8 +1284,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);
@@ -1353,7 +1340,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());
@@ -1372,9 +1359,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();
@@ -1391,8 +1378,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
@@ -1415,14 +1400,21 @@ SceneTree::SceneTree() {
#endif // _3D_DISABLED
// Initialize network state.
- set_multiplayer(Ref<MultiplayerAPI>(memnew(MultiplayerAPI)));
+ set_multiplayer(MultiplayerAPI::create_default_interface());
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)"));
@@ -1470,18 +1462,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 a34aa8e2cd..a460e40597 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();
@@ -90,8 +94,8 @@ private:
Window *root = nullptr;
uint64_t tree_version = 1;
- double physics_process_time = 1.0;
- double process_time = 1.0;
+ double physics_process_time = 0.0;
+ double process_time = 0.0;
bool accept_quit = true;
bool quit_on_go_back = true;
@@ -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 0049359cad..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_variable_set_override(*r, tex_rid);
+ RS::get_singleton()->global_shader_parameter_set_override(*r, tex_rid);
} else {
- RS::get_singleton()->global_variable_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_variable_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_variable_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;
@@ -155,7 +155,7 @@ void ShaderGlobalsOverride::_get_property_list(List<PropertyInfo> *p_list) const
pinfo.type = Variant::VECTOR3;
} break;
case RS::GLOBAL_VAR_TYPE_VEC4: {
- pinfo.type = Variant::QUATERNION;
+ pinfo.type = Variant::VECTOR4;
} break;
case RS::GLOBAL_VAR_TYPE_RECT2: {
pinfo.type = Variant::RECT2;
@@ -169,15 +169,15 @@ void ShaderGlobalsOverride::_get_property_list(List<PropertyInfo> *p_list) const
case RS::GLOBAL_VAR_TYPE_MAT3: {
pinfo.type = Variant::BASIS;
} break;
+ case RS::GLOBAL_VAR_TYPE_MAT4: {
+ pinfo.type = Variant::PROJECTION;
+ } break;
case RS::GLOBAL_VAR_TYPE_TRANSFORM_2D: {
pinfo.type = Variant::TRANSFORM2D;
} break;
case RS::GLOBAL_VAR_TYPE_TRANSFORM: {
pinfo.type = Variant::TRANSFORM3D;
} break;
- case RS::GLOBAL_VAR_TYPE_MAT4: {
- pinfo.type = Variant::PACKED_INT32_ARRAY;
- } break;
case RS::GLOBAL_VAR_TYPE_SAMPLER2D: {
pinfo.type = Variant::OBJECT;
pinfo.hint = PROPERTY_HINT_RESOURCE_TYPE;
@@ -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_variable_set_override(E.key, tex_rid);
+ RS::get_singleton()->global_shader_parameter_set_override(E.key, tex_rid);
} else {
- RS::get_singleton()->global_variable_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_variable_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 0031abd953..ad40346c36 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: {
@@ -496,6 +499,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 +635,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 +802,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;
}
@@ -1022,7 +1059,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 +1140,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);
}
@@ -1125,8 +1162,6 @@ void Viewport::_gui_cancel_tooltip() {
}
if (gui.tooltip_popup) {
gui.tooltip_popup->queue_delete();
- gui.tooltip_popup = nullptr;
- gui.tooltip_label = nullptr;
}
}
@@ -1189,8 +1224,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) {
@@ -1215,13 +1248,14 @@ void Viewport::_gui_show_tooltip() {
panel->connect("mouse_entered", callable_mp(this, &Viewport::_gui_cancel_tooltip));
}
- base_tooltip->set_anchors_and_offsets_preset(Control::PRESET_WIDE);
+ base_tooltip->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
panel->set_transient(true);
panel->set_flag(Window::FLAG_NO_FOCUS, true);
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;
@@ -1229,18 +1263,29 @@ void Viewport::_gui_show_tooltip() {
Point2 tooltip_offset = ProjectSettings::get_singleton()->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();
if (r.size.x + r.position.x > vr.size.x + vr.position.x) {
- r.position.x = vr.position.x + vr.size.x - r.size.x;
+ // Place it in the opposite direction. If it fails, just hug the border.
+ r.position.x = gui.tooltip_pos.x - r.size.x - tooltip_offset.x;
+
+ if (r.position.x < vr.position.x) {
+ r.position.x = vr.position.x + vr.size.x - r.size.x;
+ }
} else if (r.position.x < vr.position.x) {
r.position.x = vr.position.x;
}
if (r.size.y + r.position.y > vr.size.y + vr.position.y) {
- r.position.y = vr.position.y + vr.size.y - r.size.y;
+ // Same as above.
+ r.position.y = gui.tooltip_pos.y - r.size.y - tooltip_offset.y;
+
+ if (r.position.y < vr.position.y) {
+ r.position.y = vr.position.y + vr.size.y - r.size.y;
+ }
} else if (r.position.y < vr.position.y) {
r.position.y = vr.position.y;
}
@@ -1359,10 +1404,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.
}
@@ -1446,7 +1487,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) {
@@ -1536,7 +1576,7 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
gui.drag_preview_id = ObjectID();
}
_propagate_viewport_notification(this, NOTIFICATION_DRAG_END);
- // Change mouse accordingly.
+ get_base_window()->update_mouse_cursor_shape();
}
_gui_cancel_tooltip();
@@ -1557,7 +1597,7 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
gui.dragging = false;
gui.drag_mouse_over = nullptr;
_propagate_viewport_notification(this, NOTIFICATION_DRAG_END);
- // Change mouse accordingly.
+ get_base_window()->update_mouse_cursor_shape();
}
gui.mouse_focus_mask &= ~mouse_button_to_mask(mb->get_button_index()); // Remove from mask.
@@ -1600,8 +1640,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();
@@ -1698,15 +1736,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();
@@ -1865,7 +1895,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;
@@ -2091,7 +2123,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();
}
@@ -2192,7 +2224,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() {
@@ -2488,17 +2520,20 @@ bool Viewport::_sub_windows_forward_input(const Ref<InputEvent> &p_event) {
if (gui.subwindow_drag == SUB_WINDOW_DRAG_RESIZE) {
Vector2i diff = mm->get_position() - gui.subwindow_drag_from;
Size2i min_size = gui.subwindow_focused->get_min_size();
+
+ Size2i min_size_adjusted = min_size;
if (gui.subwindow_focused->is_wrapping_controls()) {
Size2i cms = gui.subwindow_focused->get_contents_minimum_size();
- min_size.x = MAX(cms.x, min_size.x);
- min_size.y = MAX(cms.y, min_size.y);
+ min_size_adjusted.x = MAX(cms.x, min_size.x);
+ min_size_adjusted.y = MAX(cms.y, min_size.y);
}
- min_size.x = MAX(min_size.x, 1);
- min_size.y = MAX(min_size.y, 1);
+
+ min_size_adjusted.x = MAX(min_size_adjusted.x, 1);
+ min_size_adjusted.y = MAX(min_size_adjusted.y, 1);
Rect2i r = gui.subwindow_resize_from_rect;
- Size2i limit = r.size - min_size;
+ Size2i limit = r.size - min_size_adjusted;
switch (gui.subwindow_resize_mode) {
case SUB_WINDOW_RESIZE_TOP_LEFT: {
@@ -2553,6 +2588,19 @@ bool Viewport::_sub_windows_forward_input(const Ref<InputEvent> &p_event) {
}
}
+ Size2i max_size = gui.subwindow_focused->get_max_size();
+ if ((max_size.x > 0 || max_size.y > 0) && (max_size.x >= min_size.x && max_size.y >= min_size.y)) {
+ max_size.x = MAX(max_size.x, 1);
+ max_size.y = MAX(max_size.y, 1);
+
+ if (r.size.x > max_size.x) {
+ r.size.x = max_size.x;
+ }
+ if (r.size.y > max_size.y) {
+ r.size.y = max_size.y;
+ }
+ }
+
gui.subwindow_focused->_rect_changed_callback(r);
}
@@ -2590,7 +2638,7 @@ bool Viewport::_sub_windows_forward_input(const Ref<InputEvent> &p_event) {
Ref<Texture2D> close_icon = sw.window->get_theme_icon(SNAME("close"));
Rect2 close_rect;
- close_rect.position = Vector2(r.position.x + r.size.x - close_v_ofs, r.position.y - close_h_ofs);
+ close_rect.position = Vector2(r.position.x + r.size.x - close_h_ofs, r.position.y - close_v_ofs);
close_rect.size = close_icon->get_size();
if (gui.subwindow_focused != sw.window) {
@@ -2658,7 +2706,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.
}
@@ -2703,6 +2753,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;
@@ -2747,7 +2802,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);
}
@@ -2820,11 +2875,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;
}
@@ -2842,7 +2897,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();
}
}
@@ -2850,17 +2905,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 == p_msaa) {
+ if (msaa_2d == p_msaa) {
return;
}
- msaa = p_msaa;
- RS::get_singleton()->viewport_set_msaa(viewport, RS::ViewportMSAA(p_msaa));
+ 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;
}
-Viewport::MSAA Viewport::get_msaa() const {
- return msaa;
+void Viewport::set_msaa_3d(MSAA p_msaa) {
+ ERR_FAIL_INDEX(p_msaa, MSAA_MAX);
+ if (msaa_3d == p_msaa) {
+ return;
+ }
+ msaa_3d = p_msaa;
+ RS::get_singleton()->viewport_set_msaa_3d(viewport, RS::ViewportMSAA(p_msaa));
+}
+
+Viewport::MSAA Viewport::get_msaa_3d() const {
+ return msaa_3d;
}
void Viewport::set_screen_space_aa(ScreenSpaceAA p_screen_space_aa) {
@@ -3569,9 +3637,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() {
@@ -3621,17 +3700,17 @@ float Viewport::get_fsr_sharpness() const {
return fsr_sharpness;
}
-void Viewport::set_fsr_mipmap_bias(float p_fsr_mipmap_bias) {
- if (fsr_mipmap_bias == p_fsr_mipmap_bias) {
+void Viewport::set_texture_mipmap_bias(float p_texture_mipmap_bias) {
+ if (texture_mipmap_bias == p_texture_mipmap_bias) {
return;
}
- fsr_mipmap_bias = p_fsr_mipmap_bias;
- RS::get_singleton()->viewport_set_fsr_mipmap_bias(viewport, p_fsr_mipmap_bias);
+ texture_mipmap_bias = p_texture_mipmap_bias;
+ RS::get_singleton()->viewport_set_texture_mipmap_bias(viewport, p_texture_mipmap_bias);
}
-float Viewport::get_fsr_mipmap_bias() const {
- return fsr_mipmap_bias;
+float Viewport::get_texture_mipmap_bias() const {
+ return texture_mipmap_bias;
}
#endif // _3D_DISABLED
@@ -3647,13 +3726,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);
@@ -3773,8 +3856,8 @@ void Viewport::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_fsr_sharpness", "fsr_sharpness"), &Viewport::set_fsr_sharpness);
ClassDB::bind_method(D_METHOD("get_fsr_sharpness"), &Viewport::get_fsr_sharpness);
- ClassDB::bind_method(D_METHOD("set_fsr_mipmap_bias", "fsr_mipmap_bias"), &Viewport::set_fsr_mipmap_bias);
- ClassDB::bind_method(D_METHOD("get_fsr_mipmap_bias"), &Viewport::get_fsr_mipmap_bias);
+ ClassDB::bind_method(D_METHOD("set_texture_mipmap_bias", "texture_mipmap_bias"), &Viewport::set_texture_mipmap_bias);
+ ClassDB::bind_method(D_METHOD("get_texture_mipmap_bias"), &Viewport::get_texture_mipmap_bias);
ClassDB::bind_method(D_METHOD("set_vrs_mode", "mode"), &Viewport::set_vrs_mode);
ClassDB::bind_method(D_METHOD("get_vrs_mode"), &Viewport::get_vrs_mode);
@@ -3793,7 +3876,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");
@@ -3804,7 +3888,7 @@ void Viewport::_bind_methods() {
ADD_GROUP("Scaling 3D", "");
ADD_PROPERTY(PropertyInfo(Variant::INT, "scaling_3d_mode", PROPERTY_HINT_ENUM, "Bilinear (Fastest),FSR 1.0 (Fast)"), "set_scaling_3d_mode", "get_scaling_3d_mode");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "scaling_3d_scale", PROPERTY_HINT_RANGE, "0.25,2.0,0.01"), "set_scaling_3d_scale", "get_scaling_3d_scale");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fsr_mipmap_bias", PROPERTY_HINT_RANGE, "-2,2,0.1"), "set_fsr_mipmap_bias", "get_fsr_mipmap_bias");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "texture_mipmap_bias", PROPERTY_HINT_RANGE, "-2,2,0.001"), "set_texture_mipmap_bias", "get_texture_mipmap_bias");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fsr_sharpness", PROPERTY_HINT_RANGE, "0,2,0.1"), "set_fsr_sharpness", "get_fsr_sharpness");
#endif
ADD_GROUP("Variable Rate Shading", "vrs_");
@@ -3927,9 +4011,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;
}
}
@@ -3970,16 +4054,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 fsr_mipmap_bias = GLOBAL_GET("rendering/scaling_3d/fsr_mipmap_bias");
- set_fsr_mipmap_bias(fsr_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.
@@ -4055,7 +4133,7 @@ Transform2D SubViewport::_stretch_transform() {
Transform2D transform = Transform2D();
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);
}
diff --git a/scene/main/viewport.h b/scene/main/viewport.h
index a43e3f3ee2..6f67649ea3 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,14 +298,15 @@ 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;
Scaling3DMode scaling_3d_mode = SCALING_3D_MODE_BILINEAR;
float scaling_3d_scale = 1.0;
float fsr_sharpness = 0.2f;
- float fsr_mipmap_bias = 0.0f;
+ float texture_mipmap_bias = 0.0f;
bool use_debanding = false;
float mesh_lod_threshold = 1.0;
bool use_occlusion_culling = false;
@@ -460,6 +462,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 +525,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;
@@ -540,8 +546,8 @@ public:
void set_fsr_sharpness(float p_fsr_sharpness);
float get_fsr_sharpness() const;
- void set_fsr_mipmap_bias(float p_fsr_mipmap_bias);
- float get_fsr_mipmap_bias() const;
+ void set_texture_mipmap_bias(float p_texture_mipmap_bias);
+ float get_texture_mipmap_bias() const;
void set_use_debanding(bool p_use_debanding);
bool is_using_debanding() const;
@@ -576,7 +582,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;
@@ -709,7 +715,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();
};
@@ -779,4 +785,4 @@ VARIANT_ENUM_CAST(Viewport::RenderInfoType);
VARIANT_ENUM_CAST(Viewport::DefaultCanvasItemTextureFilter);
VARIANT_ENUM_CAST(Viewport::DefaultCanvasItemTextureRepeat);
-#endif
+#endif // VIEWPORT_H
diff --git a/scene/main/window.cpp b/scene/main/window.cpp
index 73e8f537d9..ebb11ecee8 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);
@@ -242,8 +246,8 @@ void Window::_make_window() {
window_id = DisplayServer::get_singleton()->create_sub_window(DisplayServer::WindowMode(mode), vsync_mode, f, Rect2i(position, size));
ERR_FAIL_COND(window_id == DisplayServer::INVALID_WINDOW_ID);
DisplayServer::get_singleton()->window_set_current_screen(current_screen, window_id);
- DisplayServer::get_singleton()->window_set_max_size(max_size, window_id);
- DisplayServer::get_singleton()->window_set_min_size(min_size, window_id);
+ DisplayServer::get_singleton()->window_set_max_size(Size2i(), window_id);
+ DisplayServer::get_singleton()->window_set_min_size(Size2i(), window_id);
String tr_title = atr(title);
#ifdef DEBUG_ENABLED
if (window_id == DisplayServer::MAIN_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();
@@ -596,20 +610,43 @@ void Window::_update_window_size() {
size.x = MAX(size_limit.x, size.x);
size.y = MAX(size_limit.y, size.y);
- if (max_size.x > 0 && max_size.x > min_size.x && size.x > max_size.x) {
- size.x = max_size.x;
- }
+ bool reset_min_first = false;
+
+ bool max_size_valid = false;
+ if ((max_size.x > 0 || max_size.y > 0) && (max_size.x >= min_size.x && max_size.y >= min_size.y)) {
+ max_size_valid = true;
+
+ if (size.x > max_size.x) {
+ size.x = max_size.x;
+ }
+ if (size_limit.x > max_size.x) {
+ size_limit.x = max_size.x;
+ reset_min_first = true;
+ }
- if (max_size.y > 0 && max_size.y > min_size.y && size.y > max_size.y) {
- size.y = max_size.y;
+ if (size.y > max_size.y) {
+ size.y = max_size.y;
+ }
+ if (size_limit.y > max_size.y) {
+ size_limit.y = max_size.y;
+ reset_min_first = true;
+ }
}
if (embedder) {
+ size.x = MAX(size.x, 1);
+ size.y = MAX(size.y, 1);
+
embedder->_sub_window_update(this);
} else if (window_id != DisplayServer::INVALID_WINDOW_ID) {
- DisplayServer::get_singleton()->window_set_size(size, window_id);
+ if (reset_min_first && wrap_controls) {
+ // Avoid an error if setting max_size to a value between min_size and the previous size_limit.
+ DisplayServer::get_singleton()->window_set_min_size(Size2i(), 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_max_size(max_size, window_id);
+ DisplayServer::get_singleton()->window_set_size(size, window_id);
}
//update the viewport
@@ -622,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) {
@@ -630,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;
@@ -708,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: {
@@ -720,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);
@@ -771,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;
{
@@ -823,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: {
@@ -847,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) {
@@ -953,6 +998,8 @@ void Window::set_wrap_controls(bool p_enable) {
wrap_controls = p_enable;
if (wrap_controls) {
child_controls_changed();
+ } else {
+ _update_window_size();
}
}
@@ -1002,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());
}
}
@@ -1080,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);
@@ -1106,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);
@@ -1134,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);
@@ -1187,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);
}
@@ -1209,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) {
@@ -1263,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 {
@@ -1450,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;
@@ -1485,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>();
@@ -1509,14 +1628,14 @@ 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();
if (_get_embedder()) {
- embedder_transform.translate(get_position());
+ embedder_transform.translate_local(get_position());
embedder_transform = _get_embedder()->get_screen_transform() * embedder_transform;
}
return embedder_transform * Viewport::get_screen_transform();
@@ -1654,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");
@@ -1683,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);
@@ -1698,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);
@@ -1717,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;