summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--doc/classes/AudioEffectSpectrumAnalyzer.xml1
-rw-r--r--doc/classes/DisplayServer.xml11
-rw-r--r--doc/classes/EditorSettings.xml2
-rw-r--r--doc/classes/StatusIndicator.xml8
-rw-r--r--editor/engine_update_label.cpp52
-rw-r--r--editor/gui/editor_spin_slider.cpp15
-rw-r--r--modules/gdscript/gdscript.cpp23
-rw-r--r--modules/gdscript/gdscript.h2
-rw-r--r--modules/gdscript/gdscript_analyzer.cpp31
-rw-r--r--modules/gdscript/gdscript_parser.h5
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/static_func_access_non_static.gd10
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/static_func_access_non_static.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/static_func_access_non_static_in_lambda_param.gd15
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/static_func_access_non_static_in_lambda_param.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/static_func_call_non_static.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/static_func_call_non_static_in_lambda.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/static_func_call_non_static_in_lambda_param.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/static_var_init_access_non_static_in_lambda.gd14
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/static_var_init_access_non_static_in_lambda.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/static_var_init_access_non_static_in_lambda_setter.gd15
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/static_var_init_access_non_static_in_lambda_setter.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/static_var_init_call_non_static_in_lambda_setter.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/static_var_init_non_static_access.gd11
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/static_var_init_non_static_access.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/static_non_static_access.gd75
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/static_non_static_access.out1
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/onready_base_before_subclass.gd18
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/onready_base_before_subclass.out3
-rw-r--r--modules/mobile_vr/doc_classes/MobileVRInterface.xml3
-rw-r--r--modules/mobile_vr/mobile_vr_interface.cpp20
-rw-r--r--modules/mobile_vr/mobile_vr_interface.h5
-rw-r--r--platform/macos/display_server_macos.h1
-rw-r--r--platform/macos/display_server_macos.mm24
-rw-r--r--platform/windows/display_server_windows.cpp24
-rw-r--r--platform/windows/display_server_windows.h1
-rw-r--r--scene/main/status_indicator.cpp8
-rw-r--r--scene/main/status_indicator.h2
-rw-r--r--servers/display_server.cpp6
-rw-r--r--servers/display_server.h1
39 files changed, 366 insertions, 59 deletions
diff --git a/doc/classes/AudioEffectSpectrumAnalyzer.xml b/doc/classes/AudioEffectSpectrumAnalyzer.xml
index d9312cc87d..fbc0c2275f 100644
--- a/doc/classes/AudioEffectSpectrumAnalyzer.xml
+++ b/doc/classes/AudioEffectSpectrumAnalyzer.xml
@@ -9,7 +9,6 @@
</description>
<tutorials>
<link title="Audio Spectrum Visualizer Demo">https://godotengine.org/asset-library/asset/2762</link>
- <link title="Godot 3.2 will get new audio features">https://godotengine.org/article/godot-32-will-get-new-audio-features</link>
</tutorials>
<members>
<member name="buffer_length" type="float" setter="set_buffer_length" getter="get_buffer_length" default="2.0">
diff --git a/doc/classes/DisplayServer.xml b/doc/classes/DisplayServer.xml
index fe67c2a38e..a054048266 100644
--- a/doc/classes/DisplayServer.xml
+++ b/doc/classes/DisplayServer.xml
@@ -1167,12 +1167,21 @@
[b]Note:[/b] This method is implemented on Android, iOS, macOS, Windows, and Linux (X11/Wayland).
</description>
</method>
+ <method name="status_indicator_get_rect" qualifiers="const">
+ <return type="Rect2" />
+ <param index="0" name="id" type="int" />
+ <description>
+ Returns the rectangle for the given status indicator [param id] in screen coordinates. If the status indicator is not visible, returns an empty [Rect2].
+ [b]Note:[/b] This method is implemented on macOS and Windows.
+ </description>
+ </method>
<method name="status_indicator_set_callback">
<return type="void" />
<param index="0" name="id" type="int" />
<param index="1" name="callback" type="Callable" />
<description>
Sets the application status indicator activation callback.
+ [b]Note:[/b] This method is implemented on macOS and Windows.
</description>
</method>
<method name="status_indicator_set_icon">
@@ -1181,6 +1190,7 @@
<param index="1" name="icon" type="Texture2D" />
<description>
Sets the application status indicator icon.
+ [b]Note:[/b] This method is implemented on macOS and Windows.
</description>
</method>
<method name="status_indicator_set_menu">
@@ -1200,6 +1210,7 @@
<param index="1" name="tooltip" type="String" />
<description>
Sets the application status indicator tooltip.
+ [b]Note:[/b] This method is implemented on macOS and Windows.
</description>
</method>
<method name="tablet_get_current_driver" qualifiers="const">
diff --git a/doc/classes/EditorSettings.xml b/doc/classes/EditorSettings.xml
index 93a7b09fce..0c08dd3605 100644
--- a/doc/classes/EditorSettings.xml
+++ b/doc/classes/EditorSettings.xml
@@ -881,7 +881,7 @@
All update modes will ignore builds with different major versions (e.g. Godot 4 -&gt; Godot 5).
</member>
<member name="network/connection/network_mode" type="int" setter="" getter="">
- Determines whether online features are enabled in the editor, such as the Asset Library or update checks. Disabling these online features helps alleviate privacy concerns by preventing the editor from making HTTP requests to the Godot website, GitHub, or third-party platforms hosting assets from the Asset Library.
+ Determines whether online features are enabled in the editor, such as the Asset Library or update checks. Disabling these online features helps alleviate privacy concerns by preventing the editor from making HTTP requests to the Godot website or third-party platforms hosting assets from the Asset Library.
</member>
<member name="network/debug/remote_host" type="String" setter="" getter="">
The address to listen to when starting the remote debugger. This can be set to [code]0.0.0.0[/code] to allow external clients to connect to the remote debugger (instead of restricting the remote debugger to connections from [code]localhost[/code]).
diff --git a/doc/classes/StatusIndicator.xml b/doc/classes/StatusIndicator.xml
index fb156b3c9f..688840b17f 100644
--- a/doc/classes/StatusIndicator.xml
+++ b/doc/classes/StatusIndicator.xml
@@ -8,6 +8,14 @@
</description>
<tutorials>
</tutorials>
+ <methods>
+ <method name="get_rect" qualifiers="const">
+ <return type="Rect2" />
+ <description>
+ Returns the status indicator rectangle in screen coordinates. If this status indicator is not visible, returns an empty [Rect2].
+ </description>
+ </method>
+ </methods>
<members>
<member name="icon" type="Texture2D" setter="set_icon" getter="get_icon">
Status indicator icon.
diff --git a/editor/engine_update_label.cpp b/editor/engine_update_label.cpp
index 1d7df806de..9984d6f02f 100644
--- a/editor/engine_update_label.cpp
+++ b/editor/engine_update_label.cpp
@@ -30,6 +30,7 @@
#include "engine_update_label.h"
+#include "core/io/json.h"
#include "core/os/time.h"
#include "editor/editor_settings.h"
#include "editor/editor_string_names.h"
@@ -46,7 +47,7 @@ bool EngineUpdateLabel::_can_check_updates() const {
void EngineUpdateLabel::_check_update() {
checked_update = true;
_set_status(UpdateStatus::BUSY);
- http->request("https://raw.githubusercontent.com/godotengine/godot-website/master/_data/versions.yml");
+ http->request("https://godotengine.org/versions.json");
}
void EngineUpdateLabel::_http_request_completed(int p_result, int p_response_code, const PackedStringArray &p_headers, const PackedByteArray &p_body) {
@@ -62,12 +63,24 @@ void EngineUpdateLabel::_http_request_completed(int p_result, int p_response_cod
return;
}
- PackedStringArray lines;
+ Array version_data;
{
String s;
const uint8_t *r = p_body.ptr();
s.parse_utf8((const char *)r, p_body.size());
- lines = s.split("\n");
+
+ Variant result = JSON::parse_string(s);
+ if (result == Variant()) {
+ _set_status(UpdateStatus::ERROR);
+ _set_message(TTR("Failed to parse version JSON."), theme_cache.error_color);
+ return;
+ }
+ if (result.get_type() != Variant::ARRAY) {
+ _set_status(UpdateStatus::ERROR);
+ _set_message(TTR("Received JSON data is not a valid version array."), theme_cache.error_color);
+ return;
+ }
+ version_data = result;
}
UpdateMode update_mode = UpdateMode(int(EDITOR_GET("network/connection/engine_version_update_mode")));
@@ -78,14 +91,11 @@ void EngineUpdateLabel::_http_request_completed(int p_result, int p_response_cod
int current_minor = version_info["minor"];
int current_patch = version_info["patch"];
- int current_version_line = -1;
- for (int i = 0; i < lines.size(); i++) {
- const String &line = lines[i];
- if (!line.begins_with("- name")) {
- continue;
- }
+ Dictionary found_version_info;
+ for (const Variant &data_bit : version_data) {
+ const Dictionary info = data_bit;
- const String version_string = _extract_sub_string(line);
+ const String version_string = info["name"];
const PackedStringArray version_bits = version_string.split(".");
if (version_bits.size() < 2) {
@@ -111,7 +121,7 @@ void EngineUpdateLabel::_http_request_completed(int p_result, int p_response_cod
}
if (minor > current_minor || patch > current_patch) {
- String version_type = _extract_sub_string(lines[i + 1]);
+ String version_type = info["flavor"];
if (stable_only && _get_version_type(version_type, nullptr) != VersionType::STABLE) {
continue;
}
@@ -120,17 +130,17 @@ void EngineUpdateLabel::_http_request_completed(int p_result, int p_response_cod
found_version += "-" + version_type;
break;
} else if (minor == current_minor && patch == current_patch) {
- current_version_line = i;
+ found_version_info = info;
found_version = version_string;
break;
}
}
- if (current_version_line == -1 && !found_version.is_empty()) {
+ if (found_version_info.is_empty() && !found_version.is_empty()) {
_set_status(UpdateStatus::UPDATE_AVAILABLE);
_set_message(vformat(TTR("Update available: %s."), found_version), theme_cache.update_color);
return;
- } else if (current_version_line == -1 || stable_only) {
+ } else if (found_version_info.is_empty() || stable_only) {
_set_status(UpdateStatus::UP_TO_DATE);
return;
}
@@ -138,17 +148,11 @@ void EngineUpdateLabel::_http_request_completed(int p_result, int p_response_cod
int current_version_index;
VersionType current_version_type = _get_version_type(version_info["status"], &current_version_index);
- for (int i = current_version_line + 1; i < lines.size(); i++) {
- const String &line = lines[i];
- if (line.begins_with("- name")) {
- break;
- }
-
- if (!line.begins_with(" - name") && !line.begins_with(" flavor")) {
- continue;
- }
+ const Array releases = found_version_info["releases"];
+ for (const Variant &data_bit : version_data) {
+ const Dictionary info = data_bit;
- const String version_string = _extract_sub_string(line);
+ const String version_string = info["name"];
int version_index;
VersionType version_type = _get_version_type(version_string, &version_index);
diff --git a/editor/gui/editor_spin_slider.cpp b/editor/gui/editor_spin_slider.cpp
index c0a704105c..814484b885 100644
--- a/editor/gui/editor_spin_slider.cpp
+++ b/editor/gui/editor_spin_slider.cpp
@@ -59,17 +59,16 @@ void EditorSpinSlider::gui_input(const Ref<InputEvent> &p_event) {
if (mb.is_valid()) {
if (mb->get_button_index() == MouseButton::LEFT) {
if (mb->is_pressed()) {
- if (updown_offset != -1 && mb->get_position().x > updown_offset) {
- //there is an updown, so use it.
+ if (updown_offset != -1 && ((!is_layout_rtl() && mb->get_position().x > updown_offset) || (is_layout_rtl() && mb->get_position().x < updown_offset))) {
+ // Updown pressed.
if (mb->get_position().y < get_size().height / 2) {
set_value(get_value() + get_step());
} else {
set_value(get_value() - get_step());
}
return;
- } else {
- _grab_start();
}
+ _grab_start();
} else {
_grab_end();
}
@@ -121,7 +120,7 @@ void EditorSpinSlider::gui_input(const Ref<InputEvent> &p_event) {
}
}
} else if (updown_offset != -1) {
- bool new_hover = (mm->get_position().x > updown_offset);
+ bool new_hover = (!is_layout_rtl() && mm->get_position().x > updown_offset) || (is_layout_rtl() && mm->get_position().x < updown_offset);
if (new_hover != hover_updown) {
hover_updown = new_hover;
queue_redraw();
@@ -296,11 +295,9 @@ void EditorSpinSlider::_update_value_input_stylebox() {
// higher margin to match the location where the text begins.
// The margin values below were determined by empirical testing.
if (is_layout_rtl()) {
- stylebox->set_content_margin(SIDE_LEFT, 0);
stylebox->set_content_margin(SIDE_RIGHT, (!get_label().is_empty() ? 23 : 16) * EDSCALE);
} else {
stylebox->set_content_margin(SIDE_LEFT, (!get_label().is_empty() ? 23 : 16) * EDSCALE);
- stylebox->set_content_margin(SIDE_RIGHT, 0);
}
value_input->add_theme_style_override("normal", stylebox);
@@ -394,6 +391,9 @@ void EditorSpinSlider::_draw_spin_slider() {
c *= Color(1.2, 1.2, 1.2);
}
draw_texture(updown2, Vector2(updown_offset, updown_vofs), c);
+ if (rtl) {
+ updown_offset += updown2->get_width();
+ }
if (grabber->is_visible()) {
grabber->hide();
}
@@ -705,6 +705,7 @@ void EditorSpinSlider::_ensure_input_popup() {
}
value_input_popup = memnew(Control);
+ value_input_popup->set_anchors_and_offsets_preset(PRESET_FULL_RECT);
add_child(value_input_popup);
value_input = memnew(LineEdit);
diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp
index f238958f25..73abf71bde 100644
--- a/modules/gdscript/gdscript.cpp
+++ b/modules/gdscript/gdscript.cpp
@@ -1958,19 +1958,22 @@ int GDScriptInstance::get_method_argument_count(const StringName &p_method, bool
return 0;
}
+void GDScriptInstance::_call_implicit_ready_recursively(GDScript *p_script) {
+ // Call base class first.
+ if (p_script->_base) {
+ _call_implicit_ready_recursively(p_script->_base);
+ }
+ if (p_script->implicit_ready) {
+ Callable::CallError err;
+ p_script->implicit_ready->call(this, nullptr, 0, err);
+ }
+}
+
Variant GDScriptInstance::callp(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
GDScript *sptr = script.ptr();
if (unlikely(p_method == SNAME("_ready"))) {
- // Call implicit ready first, including for the super classes.
- while (sptr) {
- if (sptr->implicit_ready) {
- sptr->implicit_ready->call(this, nullptr, 0, r_error);
- }
- sptr = sptr->_base;
- }
-
- // Reset this back for the regular call.
- sptr = script.ptr();
+ // Call implicit ready first, including for the super classes recursively.
+ _call_implicit_ready_recursively(sptr);
}
while (sptr) {
HashMap<StringName, GDScriptFunction *>::Iterator E = sptr->member_functions.find(p_method);
diff --git a/modules/gdscript/gdscript.h b/modules/gdscript/gdscript.h
index 7bd68ac0b1..51267ecb84 100644
--- a/modules/gdscript/gdscript.h
+++ b/modules/gdscript/gdscript.h
@@ -365,6 +365,8 @@ class GDScriptInstance : public ScriptInstance {
SelfList<GDScriptFunctionState>::List pending_func_states;
+ void _call_implicit_ready_recursively(GDScript *p_script);
+
public:
virtual Object *get_owner() { return owner; }
diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp
index 28a44357eb..ec20811385 100644
--- a/modules/gdscript/gdscript_analyzer.cpp
+++ b/modules/gdscript/gdscript_analyzer.cpp
@@ -3384,7 +3384,7 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
}
if (parent_function) {
- push_error(vformat(R"*(Cannot call non-static function "%s()" from static function "%s()".)*", p_call->function_name, parent_function->identifier->name), p_call);
+ push_error(vformat(R"*(Cannot call non-static function "%s()" from the static function "%s()".)*", p_call->function_name, parent_function->identifier->name), p_call);
} else {
push_error(vformat(R"*(Cannot call non-static function "%s()" from a static variable initializer.)*", p_call->function_name), p_call);
}
@@ -3801,6 +3801,8 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
if (is_base && (!base.is_meta_type || member.function->is_static || is_constructor)) {
p_identifier->set_datatype(make_callable_type(member.function->info));
p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_FUNCTION;
+ p_identifier->function_source = member.function;
+ p_identifier->function_source_is_static = member.function->is_static;
return;
}
} break;
@@ -3849,6 +3851,7 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
if (method_info.name == p_identifier->name) {
p_identifier->set_datatype(make_callable_type(method_info));
p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_FUNCTION;
+ p_identifier->function_source_is_static = method_info.flags & METHOD_FLAG_STATIC;
return;
}
@@ -4029,25 +4032,37 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident
}
if (found_source) {
- bool source_is_variable = p_identifier->source == GDScriptParser::IdentifierNode::MEMBER_VARIABLE || p_identifier->source == GDScriptParser::IdentifierNode::INHERITED_VARIABLE;
- bool source_is_signal = p_identifier->source == GDScriptParser::IdentifierNode::MEMBER_SIGNAL;
- if ((source_is_variable || source_is_signal) && static_context) {
+ const bool source_is_instance_variable = p_identifier->source == GDScriptParser::IdentifierNode::MEMBER_VARIABLE || p_identifier->source == GDScriptParser::IdentifierNode::INHERITED_VARIABLE;
+ const bool source_is_instance_function = p_identifier->source == GDScriptParser::IdentifierNode::MEMBER_FUNCTION && !p_identifier->function_source_is_static;
+ const bool source_is_signal = p_identifier->source == GDScriptParser::IdentifierNode::MEMBER_SIGNAL;
+
+ if (static_context && (source_is_instance_variable || source_is_instance_function || source_is_signal)) {
// Get the parent function above any lambda.
GDScriptParser::FunctionNode *parent_function = parser->current_function;
while (parent_function && parent_function->source_lambda) {
parent_function = parent_function->source_lambda->parent_function;
}
+ String source_type;
+ if (source_is_instance_variable) {
+ source_type = "non-static variable";
+ } else if (source_is_instance_function) {
+ source_type = "non-static function";
+ } else { // source_is_signal
+ source_type = "signal";
+ }
+
if (parent_function) {
- push_error(vformat(R"*(Cannot access %s "%s" from the static function "%s()".)*", source_is_signal ? "signal" : "instance variable", p_identifier->name, parent_function->identifier->name), p_identifier);
+ push_error(vformat(R"*(Cannot access %s "%s" from the static function "%s()".)*", source_type, p_identifier->name, parent_function->identifier->name), p_identifier);
} else {
- push_error(vformat(R"*(Cannot access %s "%s" from a static variable initializer.)*", source_is_signal ? "signal" : "instance variable", p_identifier->name), p_identifier);
+ push_error(vformat(R"*(Cannot access %s "%s" from a static variable initializer.)*", source_type, p_identifier->name), p_identifier);
}
}
if (current_lambda != nullptr) {
- // If the identifier is a member variable (including the native class properties) or a signal, we consider the lambda to be using `self`, so we keep a reference to the current instance.
- if (source_is_variable || source_is_signal) {
+ // If the identifier is a member variable (including the native class properties), member function, or a signal,
+ // we consider the lambda to be using `self`, so we keep a reference to the current instance.
+ if (source_is_instance_variable || source_is_instance_function || source_is_signal) {
mark_lambda_use_self();
return; // No need to capture.
}
diff --git a/modules/gdscript/gdscript_parser.h b/modules/gdscript/gdscript_parser.h
index 7fb9ffe9a5..1e67e2d496 100644
--- a/modules/gdscript/gdscript_parser.h
+++ b/modules/gdscript/gdscript_parser.h
@@ -902,8 +902,11 @@ public:
VariableNode *variable_source;
ConstantNode *constant_source;
SignalNode *signal_source;
+ FunctionNode *function_source;
};
- FunctionNode *source_function = nullptr;
+ bool function_source_is_static = false; // For non-GDScript scripts.
+
+ FunctionNode *source_function = nullptr; // TODO: Rename to disambiguate `function_source`.
int usages = 0; // Useful for binds/iterator variable.
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/static_func_access_non_static.gd b/modules/gdscript/tests/scripts/analyzer/errors/static_func_access_non_static.gd
new file mode 100644
index 0000000000..e041aeb914
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/static_func_access_non_static.gd
@@ -0,0 +1,10 @@
+# GH-91403
+
+static func static_func():
+ print(non_static_func)
+
+func non_static_func():
+ pass
+
+func test():
+ pass
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/static_func_access_non_static.out b/modules/gdscript/tests/scripts/analyzer/errors/static_func_access_non_static.out
new file mode 100644
index 0000000000..d8d6c8bc1b
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/static_func_access_non_static.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Cannot access non-static function "non_static_func" from the static function "static_func()".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/static_func_access_non_static_in_lambda_param.gd b/modules/gdscript/tests/scripts/analyzer/errors/static_func_access_non_static_in_lambda_param.gd
new file mode 100644
index 0000000000..36bc9dbf15
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/static_func_access_non_static_in_lambda_param.gd
@@ -0,0 +1,15 @@
+# GH-91403
+
+func non_static_func():
+ pass
+
+static func static_func(
+ f := func ():
+ var g := func ():
+ print(non_static_func)
+ g.call()
+):
+ f.call()
+
+func test():
+ pass
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/static_func_access_non_static_in_lambda_param.out b/modules/gdscript/tests/scripts/analyzer/errors/static_func_access_non_static_in_lambda_param.out
new file mode 100644
index 0000000000..d8d6c8bc1b
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/static_func_access_non_static_in_lambda_param.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Cannot access non-static function "non_static_func" from the static function "static_func()".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/static_func_call_non_static.out b/modules/gdscript/tests/scripts/analyzer/errors/static_func_call_non_static.out
index b78f131345..c094c08cd8 100644
--- a/modules/gdscript/tests/scripts/analyzer/errors/static_func_call_non_static.out
+++ b/modules/gdscript/tests/scripts/analyzer/errors/static_func_call_non_static.out
@@ -1,2 +1,2 @@
GDTEST_ANALYZER_ERROR
-Cannot call non-static function "non_static_func()" from static function "static_func()".
+Cannot call non-static function "non_static_func()" from the static function "static_func()".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/static_func_call_non_static_in_lambda.out b/modules/gdscript/tests/scripts/analyzer/errors/static_func_call_non_static_in_lambda.out
index b78f131345..c094c08cd8 100644
--- a/modules/gdscript/tests/scripts/analyzer/errors/static_func_call_non_static_in_lambda.out
+++ b/modules/gdscript/tests/scripts/analyzer/errors/static_func_call_non_static_in_lambda.out
@@ -1,2 +1,2 @@
GDTEST_ANALYZER_ERROR
-Cannot call non-static function "non_static_func()" from static function "static_func()".
+Cannot call non-static function "non_static_func()" from the static function "static_func()".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/static_func_call_non_static_in_lambda_param.out b/modules/gdscript/tests/scripts/analyzer/errors/static_func_call_non_static_in_lambda_param.out
index b78f131345..c094c08cd8 100644
--- a/modules/gdscript/tests/scripts/analyzer/errors/static_func_call_non_static_in_lambda_param.out
+++ b/modules/gdscript/tests/scripts/analyzer/errors/static_func_call_non_static_in_lambda_param.out
@@ -1,2 +1,2 @@
GDTEST_ANALYZER_ERROR
-Cannot call non-static function "non_static_func()" from static function "static_func()".
+Cannot call non-static function "non_static_func()" from the static function "static_func()".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/static_var_init_access_non_static_in_lambda.gd b/modules/gdscript/tests/scripts/analyzer/errors/static_var_init_access_non_static_in_lambda.gd
new file mode 100644
index 0000000000..7ae5bea7d7
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/static_var_init_access_non_static_in_lambda.gd
@@ -0,0 +1,14 @@
+# GH-91403
+
+func non_static_func():
+ pass
+
+static var static_var = func ():
+ var f := func ():
+ var g := func ():
+ print(non_static_func)
+ g.call()
+ f.call()
+
+func test():
+ pass
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/static_var_init_access_non_static_in_lambda.out b/modules/gdscript/tests/scripts/analyzer/errors/static_var_init_access_non_static_in_lambda.out
new file mode 100644
index 0000000000..153e81b405
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/static_var_init_access_non_static_in_lambda.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Cannot access non-static function "non_static_func" from a static variable initializer.
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/static_var_init_access_non_static_in_lambda_setter.gd b/modules/gdscript/tests/scripts/analyzer/errors/static_var_init_access_non_static_in_lambda_setter.gd
new file mode 100644
index 0000000000..7479afc532
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/static_var_init_access_non_static_in_lambda_setter.gd
@@ -0,0 +1,15 @@
+# GH-91403
+
+func non_static_func():
+ pass
+
+static var static_var:
+ set(_value):
+ var f := func ():
+ var g := func ():
+ print(non_static_func)
+ g.call()
+ f.call()
+
+func test():
+ pass
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/static_var_init_access_non_static_in_lambda_setter.out b/modules/gdscript/tests/scripts/analyzer/errors/static_var_init_access_non_static_in_lambda_setter.out
new file mode 100644
index 0000000000..de43f2d3c4
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/static_var_init_access_non_static_in_lambda_setter.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Cannot access non-static function "non_static_func" from the static function "@static_var_setter()".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/static_var_init_call_non_static_in_lambda_setter.out b/modules/gdscript/tests/scripts/analyzer/errors/static_var_init_call_non_static_in_lambda_setter.out
index cdf3ab2aeb..a285b80025 100644
--- a/modules/gdscript/tests/scripts/analyzer/errors/static_var_init_call_non_static_in_lambda_setter.out
+++ b/modules/gdscript/tests/scripts/analyzer/errors/static_var_init_call_non_static_in_lambda_setter.out
@@ -1,2 +1,2 @@
GDTEST_ANALYZER_ERROR
-Cannot call non-static function "non_static_func()" from static function "@static_var_setter()".
+Cannot call non-static function "non_static_func()" from the static function "@static_var_setter()".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/static_var_init_non_static_access.gd b/modules/gdscript/tests/scripts/analyzer/errors/static_var_init_non_static_access.gd
new file mode 100644
index 0000000000..a21b2c4470
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/static_var_init_non_static_access.gd
@@ -0,0 +1,11 @@
+# GH-91403
+
+@static_unload
+
+func non_static():
+ return "non static"
+
+static var static_var = Callable(non_static)
+
+func test():
+ print("does not run")
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/static_var_init_non_static_access.out b/modules/gdscript/tests/scripts/analyzer/errors/static_var_init_non_static_access.out
new file mode 100644
index 0000000000..a95069dc4f
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/static_var_init_non_static_access.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Cannot access non-static function "non_static" from a static variable initializer.
diff --git a/modules/gdscript/tests/scripts/analyzer/features/static_non_static_access.gd b/modules/gdscript/tests/scripts/analyzer/features/static_non_static_access.gd
new file mode 100644
index 0000000000..80ceb6d1a9
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/static_non_static_access.gd
@@ -0,0 +1,75 @@
+@static_unload
+
+static var static_var
+var non_static_var
+
+signal my_signal()
+
+static func static_func():
+ pass
+
+func non_static_func():
+ pass
+
+static var test_static_var_lambda = func ():
+ static_func()
+ print(static_func)
+ static_var = 1
+ print(static_var)
+
+var test_non_static_var_lambda = func ():
+ static_func()
+ print(static_func)
+ static_var = 1
+ print(static_var)
+
+ non_static_func()
+ print(non_static_func)
+ non_static_var = 1
+ print(non_static_var)
+ my_signal.emit()
+ print(my_signal)
+
+static var test_static_var_setter:
+ set(_value):
+ static_func()
+ print(static_func)
+ static_var = 1
+ print(static_var)
+
+var test_non_static_var_setter:
+ set(_value):
+ static_func()
+ print(static_func)
+ static_var = 1
+ print(static_var)
+
+ non_static_func()
+ print(non_static_func)
+ non_static_var = 1
+ print(non_static_var)
+ my_signal.emit()
+ print(my_signal)
+
+static func test_static_func():
+ static_func()
+ print(static_func)
+ static_var = 1
+ print(static_var)
+
+func test_non_static_func():
+ static_func()
+ print(static_func)
+ static_var = 1
+ print(static_var)
+
+ non_static_func()
+ print(non_static_func)
+ non_static_var = 1
+ print(non_static_var)
+ my_signal.emit()
+ print(my_signal)
+
+func test():
+ test_static_var_lambda = null
+ test_non_static_var_lambda = null
diff --git a/modules/gdscript/tests/scripts/analyzer/features/static_non_static_access.out b/modules/gdscript/tests/scripts/analyzer/features/static_non_static_access.out
new file mode 100644
index 0000000000..d73c5eb7cd
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/static_non_static_access.out
@@ -0,0 +1 @@
+GDTEST_OK
diff --git a/modules/gdscript/tests/scripts/runtime/features/onready_base_before_subclass.gd b/modules/gdscript/tests/scripts/runtime/features/onready_base_before_subclass.gd
new file mode 100644
index 0000000000..99156adb28
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/onready_base_before_subclass.gd
@@ -0,0 +1,18 @@
+#GH-63329
+class A extends Node:
+ @onready var a := get_value("a")
+
+ func get_value(var_name: String) -> String:
+ print(var_name)
+ return var_name
+
+class B extends A:
+ @onready var b := get_value("b")
+
+ func _ready():
+ pass
+
+func test():
+ var node := B.new()
+ node._ready()
+ node.free()
diff --git a/modules/gdscript/tests/scripts/runtime/features/onready_base_before_subclass.out b/modules/gdscript/tests/scripts/runtime/features/onready_base_before_subclass.out
new file mode 100644
index 0000000000..b417ce67ca
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/onready_base_before_subclass.out
@@ -0,0 +1,3 @@
+GDTEST_OK
+a
+b
diff --git a/modules/mobile_vr/doc_classes/MobileVRInterface.xml b/modules/mobile_vr/doc_classes/MobileVRInterface.xml
index 1be8cc828d..8338054142 100644
--- a/modules/mobile_vr/doc_classes/MobileVRInterface.xml
+++ b/modules/mobile_vr/doc_classes/MobileVRInterface.xml
@@ -34,6 +34,9 @@
<member name="k2" type="float" setter="set_k2" getter="get_k2" default="0.215">
The k2 lens factor, see k1.
</member>
+ <member name="offset_rect" type="Rect2" setter="set_offset_rect" getter="get_offset_rect" default="Rect2(0, 0, 1, 1)">
+ Set the offset rect relative to the area being rendered. A length of 1 represents the whole rendering area on that axis.
+ </member>
<member name="oversample" type="float" setter="set_oversample" getter="get_oversample" default="1.5">
The oversample setting. Because of the lens distortion we have to render our buffers at a higher resolution then the screen can natively handle. A value between 1.5 and 2.0 often provides good results but at the cost of performance.
</member>
diff --git a/modules/mobile_vr/mobile_vr_interface.cpp b/modules/mobile_vr/mobile_vr_interface.cpp
index bba56f6468..d23edcd1d1 100644
--- a/modules/mobile_vr/mobile_vr_interface.cpp
+++ b/modules/mobile_vr/mobile_vr_interface.cpp
@@ -230,6 +230,9 @@ void MobileVRInterface::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_display_to_lens", "display_to_lens"), &MobileVRInterface::set_display_to_lens);
ClassDB::bind_method(D_METHOD("get_display_to_lens"), &MobileVRInterface::get_display_to_lens);
+ ClassDB::bind_method(D_METHOD("set_offset_rect", "offset_rect"), &MobileVRInterface::set_offset_rect);
+ ClassDB::bind_method(D_METHOD("get_offset_rect"), &MobileVRInterface::get_offset_rect);
+
ClassDB::bind_method(D_METHOD("set_oversample", "oversample"), &MobileVRInterface::set_oversample);
ClassDB::bind_method(D_METHOD("get_oversample"), &MobileVRInterface::get_oversample);
@@ -243,6 +246,7 @@ void MobileVRInterface::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "iod", PROPERTY_HINT_RANGE, "4.0,10.0,0.1"), "set_iod", "get_iod");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "display_width", PROPERTY_HINT_RANGE, "5.0,25.0,0.1"), "set_display_width", "get_display_width");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "display_to_lens", PROPERTY_HINT_RANGE, "5.0,25.0,0.1"), "set_display_to_lens", "get_display_to_lens");
+ ADD_PROPERTY(PropertyInfo(Variant::RECT2, "offset_rect"), "set_offset_rect", "get_offset_rect");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "oversample", PROPERTY_HINT_RANGE, "1.0,2.0,0.1"), "set_oversample", "get_oversample");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "k1", PROPERTY_HINT_RANGE, "0.1,10.0,0.0001"), "set_k1", "get_k1");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "k2", PROPERTY_HINT_RANGE, "0.1,10.0,0.0001"), "set_k2", "get_k2");
@@ -256,6 +260,14 @@ double MobileVRInterface::get_eye_height() const {
return eye_height;
}
+void MobileVRInterface::set_offset_rect(const Rect2 &p_offset_rect) {
+ offset_rect = p_offset_rect;
+}
+
+Rect2 MobileVRInterface::get_offset_rect() const {
+ return offset_rect;
+}
+
void MobileVRInterface::set_iod(const double p_iod) {
intraocular_dist = p_iod;
};
@@ -483,6 +495,8 @@ Vector<BlitToScreen> MobileVRInterface::post_draw_viewport(RID p_render_target,
// Because we are rendering to our device we must use our main viewport!
ERR_FAIL_COND_V(p_screen_rect == Rect2(), blit_to_screen);
+ Rect2 modified_screen_rect = Rect2(p_screen_rect.position + offset_rect.position * p_screen_rect.size, p_screen_rect.size * offset_rect.size);
+
// and add our blits
BlitToScreen blit;
blit.render_target = p_render_target;
@@ -494,16 +508,16 @@ Vector<BlitToScreen> MobileVRInterface::post_draw_viewport(RID p_render_target,
blit.lens_distortion.aspect_ratio = aspect;
// left eye
- blit.dst_rect = p_screen_rect;
+ blit.dst_rect = modified_screen_rect;
blit.dst_rect.size.width *= 0.5;
blit.multi_view.layer = 0;
blit.lens_distortion.eye_center.x = ((-intraocular_dist / 2.0) + (display_width / 4.0)) / (display_width / 2.0);
blit_to_screen.push_back(blit);
// right eye
- blit.dst_rect = p_screen_rect;
+ blit.dst_rect = modified_screen_rect;
blit.dst_rect.size.width *= 0.5;
- blit.dst_rect.position.x = blit.dst_rect.size.width;
+ blit.dst_rect.position.x += blit.dst_rect.size.width;
blit.multi_view.layer = 1;
blit.lens_distortion.eye_center.x = ((intraocular_dist / 2.0) - (display_width / 4.0)) / (display_width / 2.0);
blit_to_screen.push_back(blit);
diff --git a/modules/mobile_vr/mobile_vr_interface.h b/modules/mobile_vr/mobile_vr_interface.h
index f680d8aa11..e1d43fff74 100644
--- a/modules/mobile_vr/mobile_vr_interface.h
+++ b/modules/mobile_vr/mobile_vr_interface.h
@@ -62,6 +62,8 @@ private:
double display_to_lens = 4.0;
double oversample = 1.5;
+ Rect2 offset_rect = Rect2(0, 0, 1, 1); // Full screen rect.
+
double k1 = 0.215;
double k2 = 0.215;
double aspect = 1.0;
@@ -121,6 +123,9 @@ public:
void set_display_width(const double p_display_width);
double get_display_width() const;
+ void set_offset_rect(const Rect2 &p_offset_rect);
+ Rect2 get_offset_rect() const;
+
void set_display_to_lens(const double p_display_to_lens);
double get_display_to_lens() const;
diff --git a/platform/macos/display_server_macos.h b/platform/macos/display_server_macos.h
index 083e9731c9..db76b7d78a 100644
--- a/platform/macos/display_server_macos.h
+++ b/platform/macos/display_server_macos.h
@@ -436,6 +436,7 @@ public:
virtual void status_indicator_set_tooltip(IndicatorID p_id, const String &p_tooltip) override;
virtual void status_indicator_set_menu(IndicatorID p_id, const RID &p_menu_rid) override;
virtual void status_indicator_set_callback(IndicatorID p_id, const Callable &p_callback) override;
+ virtual Rect2 status_indicator_get_rect(IndicatorID p_id) const override;
virtual void delete_status_indicator(IndicatorID p_id) override;
static DisplayServer *create_func(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Error &r_error);
diff --git a/platform/macos/display_server_macos.mm b/platform/macos/display_server_macos.mm
index cfa4041147..0041848c78 100644
--- a/platform/macos/display_server_macos.mm
+++ b/platform/macos/display_server_macos.mm
@@ -3283,6 +3283,30 @@ void DisplayServerMacOS::status_indicator_set_callback(IndicatorID p_id, const C
[indicators[p_id].delegate setCallback:p_callback];
}
+Rect2 DisplayServerMacOS::status_indicator_get_rect(IndicatorID p_id) const {
+ ERR_FAIL_COND_V(!indicators.has(p_id), Rect2());
+
+ NSStatusItem *item = indicators[p_id].item;
+ NSView *v = item.button;
+ const NSRect contentRect = [v frame];
+ const NSRect nsrect = [v.window convertRectToScreen:contentRect];
+ Rect2 rect;
+
+ // Return the position of the top-left corner, for macOS the y starts at the bottom.
+ const float scale = screen_get_max_scale();
+ rect.size.x = nsrect.size.width;
+ rect.size.y = nsrect.size.height;
+ rect.size *= scale;
+ rect.position.x = nsrect.origin.x;
+ rect.position.y = (nsrect.origin.y + nsrect.size.height);
+ rect.position *= scale;
+ rect.position -= _get_screens_origin();
+ // macOS native y-coordinate relative to _get_screens_origin() is negative,
+ // Godot expects a positive value.
+ rect.position.y *= -1;
+ return rect;
+}
+
void DisplayServerMacOS::delete_status_indicator(IndicatorID p_id) {
ERR_FAIL_COND(!indicators.has(p_id));
diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp
index dbba9b4308..f101d85d58 100644
--- a/platform/windows/display_server_windows.cpp
+++ b/platform/windows/display_server_windows.cpp
@@ -3325,6 +3325,30 @@ void DisplayServerWindows::status_indicator_set_callback(IndicatorID p_id, const
indicators[p_id].callback = p_callback;
}
+Rect2 DisplayServerWindows::status_indicator_get_rect(IndicatorID p_id) const {
+ ERR_FAIL_COND_V(!indicators.has(p_id), Rect2());
+
+ NOTIFYICONIDENTIFIER nid;
+ ZeroMemory(&nid, sizeof(NOTIFYICONIDENTIFIER));
+ nid.cbSize = sizeof(NOTIFYICONIDENTIFIER);
+ nid.hWnd = windows[MAIN_WINDOW_ID].hWnd;
+ nid.uID = p_id;
+ nid.guidItem = GUID_NULL;
+
+ RECT rect;
+ if (Shell_NotifyIconGetRect(&nid, &rect) != S_OK) {
+ return Rect2();
+ }
+ Rect2 ind_rect = Rect2(Point2(rect.left, rect.top) - _get_screens_origin(), Size2(rect.right - rect.left, rect.bottom - rect.top));
+ for (int i = 0; i < get_screen_count(); i++) {
+ Rect2 screen_rect = Rect2(screen_get_position(i), screen_get_size(i));
+ if (screen_rect.encloses(ind_rect)) {
+ return ind_rect;
+ }
+ }
+ return Rect2();
+}
+
void DisplayServerWindows::delete_status_indicator(IndicatorID p_id) {
ERR_FAIL_COND(!indicators.has(p_id));
diff --git a/platform/windows/display_server_windows.h b/platform/windows/display_server_windows.h
index 12350d6b34..80f6061348 100644
--- a/platform/windows/display_server_windows.h
+++ b/platform/windows/display_server_windows.h
@@ -690,6 +690,7 @@ public:
virtual void status_indicator_set_tooltip(IndicatorID p_id, const String &p_tooltip) override;
virtual void status_indicator_set_menu(IndicatorID p_id, const RID &p_rid) override;
virtual void status_indicator_set_callback(IndicatorID p_id, const Callable &p_callback) override;
+ virtual Rect2 status_indicator_get_rect(IndicatorID p_id) const override;
virtual void delete_status_indicator(IndicatorID p_id) override;
virtual void set_context(Context p_context) override;
diff --git a/scene/main/status_indicator.cpp b/scene/main/status_indicator.cpp
index 22aa051c70..891974f68f 100644
--- a/scene/main/status_indicator.cpp
+++ b/scene/main/status_indicator.cpp
@@ -80,6 +80,7 @@ void StatusIndicator::_bind_methods() {
ClassDB::bind_method(D_METHOD("is_visible"), &StatusIndicator::is_visible);
ClassDB::bind_method(D_METHOD("set_menu", "menu"), &StatusIndicator::set_menu);
ClassDB::bind_method(D_METHOD("get_menu"), &StatusIndicator::get_menu);
+ ClassDB::bind_method(D_METHOD("get_rect"), &StatusIndicator::get_rect);
ADD_SIGNAL(MethodInfo("pressed", PropertyInfo(Variant::INT, "mouse_button"), PropertyInfo(Variant::VECTOR2I, "mouse_position")));
@@ -182,3 +183,10 @@ void StatusIndicator::set_visible(bool p_visible) {
bool StatusIndicator::is_visible() const {
return visible;
}
+
+Rect2 StatusIndicator::get_rect() const {
+ if (iid == DisplayServer::INVALID_INDICATOR_ID) {
+ return Rect2();
+ }
+ return DisplayServer::get_singleton()->status_indicator_get_rect(iid);
+}
diff --git a/scene/main/status_indicator.h b/scene/main/status_indicator.h
index cc137391a9..cd38da6e6c 100644
--- a/scene/main/status_indicator.h
+++ b/scene/main/status_indicator.h
@@ -61,6 +61,8 @@ public:
void set_visible(bool p_visible);
bool is_visible() const;
+
+ Rect2 get_rect() const;
};
#endif // STATUS_INDICATOR_H
diff --git a/servers/display_server.cpp b/servers/display_server.cpp
index f1e3479eae..fcbedbc1f8 100644
--- a/servers/display_server.cpp
+++ b/servers/display_server.cpp
@@ -730,6 +730,11 @@ void DisplayServer::status_indicator_set_callback(IndicatorID p_id, const Callab
WARN_PRINT("Status indicator not supported by this display server.");
}
+Rect2 DisplayServer::status_indicator_get_rect(IndicatorID p_id) const {
+ WARN_PRINT("Status indicator not supported by this display server.");
+ return Rect2();
+}
+
void DisplayServer::delete_status_indicator(IndicatorID p_id) {
WARN_PRINT("Status indicator not supported by this display server.");
}
@@ -983,6 +988,7 @@ void DisplayServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("status_indicator_set_tooltip", "id", "tooltip"), &DisplayServer::status_indicator_set_tooltip);
ClassDB::bind_method(D_METHOD("status_indicator_set_menu", "id", "menu_rid"), &DisplayServer::status_indicator_set_menu);
ClassDB::bind_method(D_METHOD("status_indicator_set_callback", "id", "callback"), &DisplayServer::status_indicator_set_callback);
+ ClassDB::bind_method(D_METHOD("status_indicator_get_rect", "id"), &DisplayServer::status_indicator_get_rect);
ClassDB::bind_method(D_METHOD("delete_status_indicator", "id"), &DisplayServer::delete_status_indicator);
ClassDB::bind_method(D_METHOD("tablet_get_driver_count"), &DisplayServer::tablet_get_driver_count);
diff --git a/servers/display_server.h b/servers/display_server.h
index 0391edecd4..30f6ee5ccf 100644
--- a/servers/display_server.h
+++ b/servers/display_server.h
@@ -569,6 +569,7 @@ public:
virtual void status_indicator_set_tooltip(IndicatorID p_id, const String &p_tooltip);
virtual void status_indicator_set_menu(IndicatorID p_id, const RID &p_menu_rid);
virtual void status_indicator_set_callback(IndicatorID p_id, const Callable &p_callback);
+ virtual Rect2 status_indicator_get_rect(IndicatorID p_id) const;
virtual void delete_status_indicator(IndicatorID p_id);
enum Context {