2
0
Fork 0

Merge pull request #78838 from YuriSizov/4.0-cherrypicks

Cherry-picks for the 4.0 branch (future 4.0.4) - 2nd batch
4.0
Rémi Verschelde 2023-06-30 13:44:09 +07:00 committed by GitHub
commit c8e0bd50c5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
89 changed files with 873 additions and 451 deletions

@ -5,18 +5,19 @@ body:
- type: markdown - type: markdown
attributes: attributes:
value: | value: |
- Read our [CONTRIBUTING.md guide](https://github.com/godotengine/godot/blob/master/CONTRIBUTING.md#reporting-bugs) on reporting bugs. - When reporting bugs, you'll make our life simpler (and the fix will come sooner) if you follow the guidelines in this template.
- Write a descriptive issue title above. - Write a descriptive issue title above.
- Search [open](https://github.com/godotengine/godot/issues) and [closed](https://github.com/godotengine/godot/issues?q=is%3Aissue+is%3Aclosed) issues to ensure it has not already been reported. - The golden rule is to **always open *one* issue for *one* bug**. If you notice several bugs and want to report them, make sure to create one new issue for each of them.
- Search [open](https://github.com/godotengine/godot/issues) and [closed](https://github.com/godotengine/godot/issues?q=is%3Aissue+is%3Aclosed) issues to ensure it has not already been reported. If you don't find a relevant match or if you're unsure, don't hesitate to **open a new issue**. The bugsquad will handle it from there if it's a duplicate.
- Verify that you are using a [supported Godot version](https://docs.godotengine.org/en/stable/about/release_policy.html). - Verify that you are using a [supported Godot version](https://docs.godotengine.org/en/stable/about/release_policy.html).
- type: input - type: input
attributes: attributes:
label: Godot version label: Godot version
description: > description: >
Specify the Git commit hash if using a development or non-official build. Specify the Godot version, including the Git commit hash if using a development or non-official build.
If you use a custom build, please test if your issue is reproducible in official builds too. If you use a custom build, please test if your issue is reproducible in official builds too.
placeholder: 3.3.stable, 4.0.dev (3041becc6) placeholder: 3.5.stable, 4.0.dev (3041becc6)
validations: validations:
required: true required: true
@ -24,9 +25,12 @@ body:
attributes: attributes:
label: System information label: System information
description: | description: |
Specify the OS version, and when relevant hardware information. - Specify the OS version, and when relevant hardware information.
For graphics-related issues, specify the GPU model, driver version, and the rendering backend (GLES2, GLES3, Vulkan). - For issues that are likely OS-specific and/or graphics-related, please specify the CPU model and architecture.
placeholder: Windows 10, GLES3, Intel HD Graphics 620 (27.20.100.9616) - For graphics-related issues, specify the GPU model, driver version, and the rendering backend (GLES2, GLES3, Vulkan).
- **Bug reports not including the required information may be closed at the maintainers' discretion.** If in doubt, always include all the requested information; it's better to include too much information than not enough information.
- **Starting from Godot 4.1, you can copy this information to your clipboard by using *Help > Copy System Info* at the top of the editor window.**
placeholder: Windows 10 - Godot v4.0.3.stable - Vulkan (Forward+) - dedicated NVIDIA GeForce GTX 970 (nvidia, 510.85.02) - Intel Core i7-10700KF CPU @ 3.80GHz (16 Threads)
validations: validations:
required: true required: true
@ -52,8 +56,10 @@ body:
attributes: attributes:
label: Minimal reproduction project label: Minimal reproduction project
description: | description: |
A small Godot project which reproduces the issue, with no unnecessary files included. Be sure to not include the `.godot` folder in the archive (but keep `project.godot`). - A small Godot project which reproduces the issue, with no unnecessary files included. Be sure to not include the `.godot` folder in the archive (but keep `project.godot`).
Required, unless the reproduction steps are trivial and don't require any project files to be followed. In this case, write "N/A" in the field. - Required, unless the reproduction steps are trivial and don't require any project files to be followed. In this case, write "N/A" in the field.
Drag and drop a ZIP archive to upload it. **Do not select another field until the project is done uploading.** - Drag and drop a ZIP archive to upload it. **Do not select another field until the project is done uploading.**
- **Note for C# users:** If your issue is *not* Mono-specific, please upload a minimal reproduction project written in GDScript or VisualScript. This will make it easier for contributors to reproduce the issue locally as not everyone has a Mono setup available.
- **If you've been asked by a maintainer to upload a minimal reproduction project, you *must* do so within 7 days.** Otherwise, your bug report will be closed as it'll be considered too difficult to diagnose.
validations: validations:
required: true required: true

@ -103,8 +103,8 @@ jobs:
- name: Spell checks via codespell - name: Spell checks via codespell
if: github.event_name == 'pull_request' && env.CHANGED_FILES != '' if: github.event_name == 'pull_request' && env.CHANGED_FILES != ''
uses: codespell-project/actions-codespell@v1 uses: codespell-project/actions-codespell@v2
with: with:
skip: "./bin,./thirdparty,*.desktop,*.gen.*,*.po,*.pot,*.rc,./AUTHORS.md,./COPYRIGHT.txt,./DONORS.md,./core/input/gamecontrollerdb.txt,./core/string/locales.h,./editor/project_converter_3_to_4.cpp,./misc/scripts/codespell.sh,./platform/android/java/lib/src/com,./platform/web/node_modules,./platform/web/package-lock.json" skip: "./bin,./thirdparty,*.desktop,*.gen.*,*.po,*.pot,*.rc,./AUTHORS.md,./COPYRIGHT.txt,./DONORS.md,./core/input/gamecontrollerdb.txt,./core/string/locales.h,./editor/project_converter_3_to_4.cpp,./misc/scripts/codespell.sh,./platform/android/java/lib/src/com,./platform/web/node_modules,./platform/web/package-lock.json"
ignore_words_list: "curvelinear,doubleclick,expct,findn,gird,hel,inout,lod,nd,numer,ot,te,vai" ignore_words_list: "curvelinear,doubleclick,expct,findn,gird,hel,inout,lod,mis,nd,numer,ot,te,vai"
path: ${{ env.CHANGED_FILES }} path: ${{ env.CHANGED_FILES }}

@ -8,87 +8,10 @@
- [Contributing to Godot's translation](#contributing-to-godots-translation) - [Contributing to Godot's translation](#contributing-to-godots-translation)
- [Communicating with developers](#communicating-with-developers) - [Communicating with developers](#communicating-with-developers)
**Please read the first section before reporting a bug!**
## Reporting bugs ## Reporting bugs
The golden rule is to **always open *one* issue for *one* bug**. If you notice Report bugs [here](https://github.com/godotengine/godot/issues/new?assignees=&labels=&template=bug_report.yml).
several bugs and want to report them, make sure to create one new issue for Please follow the instructions in the template when you do.
each of them.
If you're reporting a new bug, you'll make our life simpler (and the
fix will come sooner) by following these guidelines:
### Search first in the existing database
Issues are often reported several times by various users. It's good practice to
**search first in the [issue tracker](https://github.com/godotengine/godot/issues)
before reporting your issue**. If you don't find a relevant match or if you're
unsure, don't hesitate to **open a new issue**. The bugsquad will handle it
from there if it's a duplicate.
### Specify the platform
Godot runs on a large variety of platforms and operating systems and devices.
**In your bug reports, please always specify:**
- Operating system and version (e.g. Windows 10, macOS 10.15, Ubuntu 19.10)
- Godot version (e.g. 3.2, 3.1.2, or the Git commit hash if you're using a development branch)
For bugs that are likely OS-specific and/or graphics-related, please also specify:
- Device (CPU model including architecture, e.g. x86_64, arm64, etc.)
- GPU model (and the driver version in use if you know it)
**Bug reports not including the required information may be closed at the
maintainers' discretion.** If in doubt, always include all the requested
information; it's better to include too much information than not enough
information.
### Specify steps to reproduce
Many bugs can't be reproduced unless specific steps are taken. Please **specify
the exact steps** that must be taken to reproduce the condition, and try to
keep them as minimal as possible. If you're describing a procedure to follow
in the editor, don't hesitate to include screenshots.
Making your bug report easy to reproduce will make it easier for contributors
to fix the bug.
### Provide a simple example project
Sometimes, unexpected behavior can happen in your project. In such case,
understand that:
- What happens to you may not happen to other users.
- We can't take the time to look at your project, understand how it is set up
and then figure out why it's failing.
- On the contributors' end, recreating a test project from scratch takes valuable
time that can be saved by uploading a *minimal* project.
To speed up our work, **please upload a minimal project** that isolates
and reproduces the issue. This is always the **best way for us to fix it**.
We recommend attaching a ZIP file with the minimal project directly to the bug report,
by drag and dropping the file in the GitHub edition field. This ensures the file
can remain available for a long period of time. Only use third-party file hosts
if your ZIP file isn't accepted by GitHub because it's too large.
We recommend always attaching a minimal reproduction project, even if the issue
may seem simple to reproduce manually.
**Note for C# users:** If your issue is *not* .NET-specific, please upload a
minimal reproduction project written in GDScript.
This will make it easier for contributors to reproduce the issue
locally as not everyone has a .NET setup available.
**If you've been asked by a maintainer to upload a minimal reproduction project,
you *must* do so within 7 days.** Otherwise, your bug report will be closed as
it'll be considered too difficult to diagnose.
Now that you've read the guidelines, click the link below to create a
bug report:
- **[Report a bug](https://github.com/godotengine/godot/issues/new?assignees=&labels=&template=bug_report.yml)**
## Proposing features or improvements ## Proposing features or improvements

@ -640,7 +640,7 @@ Error ProjectSettings::_setup(const String &p_path, const String &p_main_pack, b
Error ProjectSettings::setup(const String &p_path, const String &p_main_pack, bool p_upwards, bool p_ignore_override) { Error ProjectSettings::setup(const String &p_path, const String &p_main_pack, bool p_upwards, bool p_ignore_override) {
Error err = _setup(p_path, p_main_pack, p_upwards, p_ignore_override); Error err = _setup(p_path, p_main_pack, p_upwards, p_ignore_override);
if (err == OK) { if (err == OK && !p_ignore_override) {
String custom_settings = GLOBAL_GET("application/config/project_settings_override"); String custom_settings = GLOBAL_GET("application/config/project_settings_override");
if (!custom_settings.is_empty()) { if (!custom_settings.is_empty()) {
_load_settings_text(custom_settings); _load_settings_text(custom_settings);

@ -635,6 +635,10 @@
If [code]true[/code], increases the scrollbar touch area to improve usability on touchscreen devices. If [code]true[/code], increases the scrollbar touch area to improve usability on touchscreen devices.
[b]Note:[/b] Defaults to [code]true[/code] on touchscreen devices. [b]Note:[/b] Defaults to [code]true[/code] on touchscreen devices.
</member> </member>
<member name="interface/touchscreen/scale_gizmo_handles" type="float" setter="" getter="">
Specify the multiplier to apply to the scale for the editor gizmo handles to improve usability on touchscreen devices.
[b]Note:[/b] Defaults to [code]1[/code] on non-touchscreen devices.
</member>
<member name="network/debug/remote_host" type="String" setter="" getter=""> <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]). 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]).
</member> </member>

@ -179,8 +179,7 @@ typedef void (*DEBUGPROCARB)(GLenum source,
typedef void (*DebugMessageCallbackARB)(DEBUGPROCARB callback, const void *userParam); typedef void (*DebugMessageCallbackARB)(DEBUGPROCARB callback, const void *userParam);
void RasterizerGLES3::initialize() { void RasterizerGLES3::initialize() {
// NVIDIA suffixes all GPU model names with "/PCIe/SSE2" in OpenGL (but not Vulkan). This isn't necessary to display nowadays, so it can be trimmed. print_line(vformat("OpenGL API %s - Compatibility - Using Device: %s - %s", RS::get_singleton()->get_video_adapter_api_version(), RS::get_singleton()->get_video_adapter_vendor(), RS::get_singleton()->get_video_adapter_name()));
print_line(vformat("OpenGL API %s - Compatibility - Using Device: %s - %s", RS::get_singleton()->get_video_adapter_api_version(), RS::get_singleton()->get_video_adapter_vendor(), RS::get_singleton()->get_video_adapter_name().trim_suffix("/PCIe/SSE2")));
} }
void RasterizerGLES3::finalize() { void RasterizerGLES3::finalize() {

@ -534,7 +534,7 @@ AABB MeshStorage::mesh_get_aabb(RID p_mesh, RID p_skeleton) {
if (skeleton->use_2d) { if (skeleton->use_2d) {
for (int j = 0; j < bs; j++) { for (int j = 0; j < bs; j++) {
if (skbones[0].size == Vector3()) { if (skbones[j].size == Vector3(-1, -1, -1)) {
continue; //bone is unused continue; //bone is unused
} }
@ -561,7 +561,7 @@ AABB MeshStorage::mesh_get_aabb(RID p_mesh, RID p_skeleton) {
} }
} else { } else {
for (int j = 0; j < bs; j++) { for (int j = 0; j < bs; j++) {
if (skbones[0].size == Vector3()) { if (skbones[j].size == Vector3(-1, -1, -1)) {
continue; //bone is unused continue; //bone is unused
} }

@ -328,11 +328,15 @@ uint64_t Utilities::get_rendering_info(RS::RenderingInfo p_info) {
} }
String Utilities::get_video_adapter_name() const { String Utilities::get_video_adapter_name() const {
return (const char *)glGetString(GL_RENDERER); const String rendering_device_name = (const char *)glGetString(GL_RENDERER);
// NVIDIA suffixes all GPU model names with "/PCIe/SSE2" in OpenGL (but not Vulkan). This isn't necessary to display nowadays, so it can be trimmed.
return rendering_device_name.trim_suffix("/PCIe/SSE2");
} }
String Utilities::get_video_adapter_vendor() const { String Utilities::get_video_adapter_vendor() const {
return (const char *)glGetString(GL_VENDOR); const String rendering_device_vendor = (const char *)glGetString(GL_VENDOR);
// NVIDIA suffixes its vendor name with " Corporation". This is neither necessary to process nor display.
return rendering_device_vendor.trim_suffix(" Corporation");
} }
RenderingDevice::DeviceType Utilities::get_video_adapter_type() const { RenderingDevice::DeviceType Utilities::get_video_adapter_type() const {

@ -3647,7 +3647,7 @@ VkRenderPass RenderingDeviceVulkan::_render_pass_create(const Vector<AttachmentF
} else { } else {
description.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; description.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
description.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; description.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
description.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; // Don't care what is there. description.finalLayout = VK_IMAGE_LAYOUT_UNDEFINED; // Don't care what is there.
// TODO: What does this mean about the next usage (and thus appropriate dependency masks. // TODO: What does this mean about the next usage (and thus appropriate dependency masks.
} }
} break; } break;
@ -5931,10 +5931,10 @@ Vector<uint8_t> RenderingDeviceVulkan::buffer_get_data(RID p_buffer, uint32_t p_
ERR_FAIL_V_MSG(Vector<uint8_t>(), "Buffer is either invalid or this type of buffer can't be retrieved. Only Index and Vertex buffers allow retrieving."); ERR_FAIL_V_MSG(Vector<uint8_t>(), "Buffer is either invalid or this type of buffer can't be retrieved. Only Index and Vertex buffers allow retrieving.");
} }
// Make sure no one is using the buffer -- the "false" gets us to the same command buffer as below. // Make sure no one is using the buffer -- the "true" gets us to the same command buffer as below.
_buffer_memory_barrier(buffer->buffer, 0, buffer->size, src_stage_mask, VK_PIPELINE_STAGE_TRANSFER_BIT, src_access_mask, VK_ACCESS_TRANSFER_READ_BIT, false); _buffer_memory_barrier(buffer->buffer, 0, buffer->size, src_stage_mask, VK_PIPELINE_STAGE_TRANSFER_BIT, src_access_mask, VK_ACCESS_TRANSFER_READ_BIT, true);
VkCommandBuffer command_buffer = frames[frame].setup_command_buffer; VkCommandBuffer command_buffer = frames[frame].draw_command_buffer;
// Size of buffer to retrieve. // Size of buffer to retrieve.
if (!p_size) { if (!p_size) {

@ -122,24 +122,12 @@ void FindReplaceBar::unhandled_input(const Ref<InputEvent> &p_event) {
ERR_FAIL_COND(p_event.is_null()); ERR_FAIL_COND(p_event.is_null());
Ref<InputEventKey> k = p_event; Ref<InputEventKey> k = p_event;
if (!k.is_valid() || !k->is_pressed()) {
return;
}
Control *focus_owner = get_viewport()->gui_get_focus_owner();
if (text_editor->has_focus() || (focus_owner && vbc_lineedit->is_ancestor_of(focus_owner))) {
bool accepted = true;
switch (k->get_keycode()) { if (k.is_valid() && k->is_action_pressed(SNAME("ui_cancel"), false, true)) {
case Key::ESCAPE: { Control *focus_owner = get_viewport()->gui_get_focus_owner();
_hide_bar();
} break;
default: {
accepted = false;
} break;
}
if (accepted) { if (text_editor->has_focus() || (focus_owner && vbc_lineedit->is_ancestor_of(focus_owner))) {
_hide_bar();
accept_event(); accept_event();
} }
} }

@ -1504,6 +1504,16 @@ void EditorFileSystem::_save_late_updated_files() {
} }
Vector<String> EditorFileSystem::_get_dependencies(const String &p_path) { Vector<String> EditorFileSystem::_get_dependencies(const String &p_path) {
// Avoid error spam on first opening of a not yet imported project by treating the following situation
// as a benign one, not letting the file open error happen: the resource is of an importable type but
// it has not been imported yet.
if (ResourceFormatImporter::get_singleton()->recognize_path(p_path)) {
const String &internal_path = ResourceFormatImporter::get_singleton()->get_internal_resource_path(p_path);
if (!internal_path.is_empty() && !FileAccess::exists(internal_path)) { // If path is empty (error), keep the code flow to the error.
return Vector<String>();
}
}
List<String> deps; List<String> deps;
ResourceLoader::get_dependencies(p_path, &deps); ResourceLoader::get_dependencies(p_path, &deps);

@ -2567,22 +2567,10 @@ void FindBar::unhandled_input(const Ref<InputEvent> &p_event) {
ERR_FAIL_COND(p_event.is_null()); ERR_FAIL_COND(p_event.is_null());
Ref<InputEventKey> k = p_event; Ref<InputEventKey> k = p_event;
if (k.is_valid()) { if (k.is_valid() && k->is_action_pressed(SNAME("ui_cancel"), false, true)) {
if (k->is_pressed() && (rich_text_label->has_focus() || is_ancestor_of(get_viewport()->gui_get_focus_owner()))) { if (rich_text_label->has_focus() || is_ancestor_of(get_viewport()->gui_get_focus_owner())) {
bool accepted = true; _hide_bar();
accept_event();
switch (k->get_keycode()) {
case Key::ESCAPE: {
_hide_bar();
} break;
default: {
accepted = false;
} break;
}
if (accepted) {
accept_event();
}
} }
} }
} }

@ -592,6 +592,9 @@ void EditorProperty::add_focusable(Control *p_control) {
void EditorProperty::select(int p_focusable) { void EditorProperty::select(int p_focusable) {
bool already_selected = selected; bool already_selected = selected;
if (!selectable) {
return;
}
if (p_focusable >= 0) { if (p_focusable >= 0) {
ERR_FAIL_INDEX(p_focusable, focusables.size()); ERR_FAIL_INDEX(p_focusable, focusables.size());
@ -665,11 +668,7 @@ void EditorProperty::gui_input(const Ref<InputEvent> &p_event) {
mpos.x = get_size().x - mpos.x; mpos.x = get_size().x - mpos.x;
} }
if (!selected && selectable) { select();
selected = true;
emit_signal(SNAME("selected"), property, -1);
queue_redraw();
}
if (keying_rect.has_point(mpos)) { if (keying_rect.has_point(mpos)) {
accept_event(); accept_event();

@ -42,25 +42,15 @@ void EditorLayoutsDialog::_line_gui_input(const Ref<InputEvent> &p_event) {
Ref<InputEventKey> k = p_event; Ref<InputEventKey> k = p_event;
if (k.is_valid()) { if (k.is_valid()) {
if (!k->is_pressed()) { if (k->is_action_pressed(SNAME("ui_accept"), false, true)) {
return; if (get_hide_on_ok()) {
}
switch (k->get_keycode()) {
case Key::KP_ENTER:
case Key::ENTER: {
if (get_hide_on_ok()) {
hide();
}
ok_pressed();
set_input_as_handled();
} break;
case Key::ESCAPE: {
hide(); hide();
set_input_as_handled(); }
} break; ok_pressed();
default: set_input_as_handled();
break; } else if (k->is_action_pressed(SNAME("ui_cancel"), false, true)) {
hide();
set_input_as_handled();
} }
} }
} }

@ -761,7 +761,9 @@ void EditorNode::_notification(int p_what) {
EditorSettings::get_singleton()->check_changed_settings_in_group("text_editor/theme") || EditorSettings::get_singleton()->check_changed_settings_in_group("text_editor/theme") ||
EditorSettings::get_singleton()->check_changed_settings_in_group("text_editor/help/help") || EditorSettings::get_singleton()->check_changed_settings_in_group("text_editor/help/help") ||
EditorSettings::get_singleton()->check_changed_settings_in_group("filesystem/file_dialog/thumbnail_size") || EditorSettings::get_singleton()->check_changed_settings_in_group("filesystem/file_dialog/thumbnail_size") ||
EditorSettings::get_singleton()->check_changed_settings_in_group("run/output/font_size"); EditorSettings::get_singleton()->check_changed_settings_in_group("run/output/font_size") ||
EditorSettings::get_singleton()->check_changed_settings_in_group("interface/touchscreen/increase_scrollbar_touch_area") ||
EditorSettings::get_singleton()->check_changed_settings_in_group("interface/touchscreen/scale_gizmo_handles");
if (theme_changed) { if (theme_changed) {
theme = create_custom_theme(theme_base->get_theme()); theme = create_custom_theme(theme_base->get_theme());
@ -836,6 +838,7 @@ void EditorNode::_notification(int p_what) {
help_menu->set_item_icon(help_menu->get_item_index(HELP_DOCS), gui_base->get_theme_icon(SNAME("ExternalLink"), SNAME("EditorIcons"))); help_menu->set_item_icon(help_menu->get_item_index(HELP_DOCS), gui_base->get_theme_icon(SNAME("ExternalLink"), SNAME("EditorIcons")));
help_menu->set_item_icon(help_menu->get_item_index(HELP_QA), gui_base->get_theme_icon(SNAME("ExternalLink"), SNAME("EditorIcons"))); help_menu->set_item_icon(help_menu->get_item_index(HELP_QA), gui_base->get_theme_icon(SNAME("ExternalLink"), SNAME("EditorIcons")));
help_menu->set_item_icon(help_menu->get_item_index(HELP_REPORT_A_BUG), gui_base->get_theme_icon(SNAME("ExternalLink"), SNAME("EditorIcons"))); help_menu->set_item_icon(help_menu->get_item_index(HELP_REPORT_A_BUG), gui_base->get_theme_icon(SNAME("ExternalLink"), SNAME("EditorIcons")));
help_menu->set_item_icon(help_menu->get_item_index(HELP_COPY_SYSTEM_INFO), gui_base->get_theme_icon(SNAME("ActionCopy"), SNAME("EditorIcons")));
help_menu->set_item_icon(help_menu->get_item_index(HELP_SUGGEST_A_FEATURE), gui_base->get_theme_icon(SNAME("ExternalLink"), SNAME("EditorIcons"))); help_menu->set_item_icon(help_menu->get_item_index(HELP_SUGGEST_A_FEATURE), gui_base->get_theme_icon(SNAME("ExternalLink"), SNAME("EditorIcons")));
help_menu->set_item_icon(help_menu->get_item_index(HELP_SEND_DOCS_FEEDBACK), gui_base->get_theme_icon(SNAME("ExternalLink"), SNAME("EditorIcons"))); help_menu->set_item_icon(help_menu->get_item_index(HELP_SEND_DOCS_FEEDBACK), gui_base->get_theme_icon(SNAME("ExternalLink"), SNAME("EditorIcons")));
help_menu->set_item_icon(help_menu->get_item_index(HELP_COMMUNITY), gui_base->get_theme_icon(SNAME("ExternalLink"), SNAME("EditorIcons"))); help_menu->set_item_icon(help_menu->get_item_index(HELP_COMMUNITY), gui_base->get_theme_icon(SNAME("ExternalLink"), SNAME("EditorIcons")));
@ -3168,6 +3171,10 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) {
case HELP_REPORT_A_BUG: { case HELP_REPORT_A_BUG: {
OS::get_singleton()->shell_open("https://github.com/godotengine/godot/issues"); OS::get_singleton()->shell_open("https://github.com/godotengine/godot/issues");
} break; } break;
case HELP_COPY_SYSTEM_INFO: {
String info = _get_system_info();
DisplayServer::get_singleton()->clipboard_set(info);
} break;
case HELP_SUGGEST_A_FEATURE: { case HELP_SUGGEST_A_FEATURE: {
OS::get_singleton()->shell_open("https://github.com/godotengine/godot-proposals#readme"); OS::get_singleton()->shell_open("https://github.com/godotengine/godot-proposals#readme");
} break; } break;
@ -4647,6 +4654,98 @@ void EditorNode::progress_end_task_bg(const String &p_task) {
singleton->progress_hb->end_task(p_task); singleton->progress_hb->end_task(p_task);
} }
String EditorNode::_get_system_info() const {
String distribution_name = OS::get_singleton()->get_distribution_name();
if (distribution_name.is_empty()) {
distribution_name = OS::get_singleton()->get_name();
}
if (distribution_name.is_empty()) {
distribution_name = "Other";
}
const String distribution_version = OS::get_singleton()->get_version();
String godot_version = "Godot v" + String(VERSION_FULL_CONFIG);
if (String(VERSION_BUILD) != "official") {
String hash = String(VERSION_HASH);
hash = hash.is_empty() ? String("unknown") : vformat("(%s)", hash.left(9));
godot_version += " " + hash;
}
String driver_name = GLOBAL_GET("rendering/rendering_device/driver");
String rendering_method = GLOBAL_GET("rendering/renderer/rendering_method");
const String rendering_device_name = RenderingServer::get_singleton()->get_video_adapter_name();
RenderingDevice::DeviceType device_type = RenderingServer::get_singleton()->get_video_adapter_type();
String device_type_string;
switch (device_type) {
case RenderingDevice::DeviceType::DEVICE_TYPE_INTEGRATED_GPU:
device_type_string = "integrated";
break;
case RenderingDevice::DeviceType::DEVICE_TYPE_DISCRETE_GPU:
device_type_string = "dedicated";
break;
case RenderingDevice::DeviceType::DEVICE_TYPE_VIRTUAL_GPU:
device_type_string = "virtual";
break;
case RenderingDevice::DeviceType::DEVICE_TYPE_CPU:
device_type_string = "(software emulation on CPU)";
break;
case RenderingDevice::DeviceType::DEVICE_TYPE_OTHER:
case RenderingDevice::DeviceType::DEVICE_TYPE_MAX:
break; // Can't happen, but silences warning for DEVICE_TYPE_MAX
}
const Vector<String> video_adapter_driver_info = OS::get_singleton()->get_video_adapter_driver_info();
const String processor_name = OS::get_singleton()->get_processor_name();
const int processor_count = OS::get_singleton()->get_processor_count();
// Prettify
if (driver_name == "vulkan") {
driver_name = "Vulkan";
} else if (driver_name == "opengl3") {
driver_name = "GLES3";
}
if (rendering_method == "forward_plus") {
rendering_method = "Forward+";
} else if (rendering_method == "mobile") {
rendering_method = "Mobile";
} else if (rendering_method == "gl_compatibility") {
rendering_method = "Compatibility";
}
// Join info.
Vector<String> info;
info.push_back(godot_version);
if (!distribution_version.is_empty()) {
info.push_back(distribution_name + " " + distribution_version);
} else {
info.push_back(distribution_name);
}
info.push_back(vformat("%s (%s)", driver_name, rendering_method));
String graphics;
if (!device_type_string.is_empty()) {
graphics = device_type_string + " ";
}
graphics += rendering_device_name;
if (video_adapter_driver_info.size() == 2) { // This vector is always either of length 0 or 2.
String vad_name = video_adapter_driver_info[0];
String vad_version = video_adapter_driver_info[1]; // Version could be potentially empty on Linux/BSD.
if (!vad_version.is_empty()) {
graphics += vformat(" (%s; %s)", vad_name, vad_version);
} else {
graphics += vformat(" (%s)", vad_name);
}
}
info.push_back(graphics);
info.push_back(vformat("%s (%d Threads)", processor_name, processor_count));
return String(" - ").join(info);
}
Ref<Texture2D> EditorNode::_file_dialog_get_icon(const String &p_path) { Ref<Texture2D> EditorNode::_file_dialog_get_icon(const String &p_path) {
EditorFileSystemDirectory *efsd = EditorFileSystem::get_singleton()->get_filesystem_path(p_path.get_base_dir()); EditorFileSystemDirectory *efsd = EditorFileSystem::get_singleton()->get_filesystem_path(p_path.get_base_dir());
if (efsd) { if (efsd) {
@ -7374,14 +7473,8 @@ EditorNode::EditorNode() {
project_menu->add_shortcut(ED_SHORTCUT_AND_COMMAND("editor/project_settings", TTR("Project Settings..."), Key::NONE, TTR("Project Settings")), RUN_SETTINGS); project_menu->add_shortcut(ED_SHORTCUT_AND_COMMAND("editor/project_settings", TTR("Project Settings..."), Key::NONE, TTR("Project Settings")), RUN_SETTINGS);
project_menu->connect("id_pressed", callable_mp(this, &EditorNode::_menu_option)); project_menu->connect("id_pressed", callable_mp(this, &EditorNode::_menu_option));
vcs_actions_menu = VersionControlEditorPlugin::get_singleton()->get_version_control_actions_panel();
vcs_actions_menu->set_name("Version Control");
vcs_actions_menu->connect("index_pressed", callable_mp(this, &EditorNode::_version_control_menu_option));
project_menu->add_separator(); project_menu->add_separator();
project_menu->add_child(vcs_actions_menu); project_menu->add_item(TTR("Version Control"), VCS_MENU);
project_menu->add_submenu_item(TTR("Version Control"), "Version Control");
vcs_actions_menu->add_item(TTR("Create Version Control Metadata"), RUN_VCS_METADATA);
vcs_actions_menu->add_item(TTR("Version Control Settings"), RUN_VCS_SETTINGS);
project_menu->add_separator(); project_menu->add_separator();
project_menu->add_shortcut(ED_SHORTCUT_AND_COMMAND("editor/export", TTR("Export..."), Key::NONE, TTR("Export")), FILE_EXPORT_PROJECT); project_menu->add_shortcut(ED_SHORTCUT_AND_COMMAND("editor/export", TTR("Export..."), Key::NONE, TTR("Export")), FILE_EXPORT_PROJECT);
@ -7501,6 +7594,8 @@ EditorNode::EditorNode() {
help_menu->add_icon_shortcut(gui_base->get_theme_icon(SNAME("ExternalLink"), SNAME("EditorIcons")), ED_SHORTCUT_AND_COMMAND("editor/online_docs", TTR("Online Documentation")), HELP_DOCS); help_menu->add_icon_shortcut(gui_base->get_theme_icon(SNAME("ExternalLink"), SNAME("EditorIcons")), ED_SHORTCUT_AND_COMMAND("editor/online_docs", TTR("Online Documentation")), HELP_DOCS);
help_menu->add_icon_shortcut(gui_base->get_theme_icon(SNAME("ExternalLink"), SNAME("EditorIcons")), ED_SHORTCUT_AND_COMMAND("editor/q&a", TTR("Questions & Answers")), HELP_QA); help_menu->add_icon_shortcut(gui_base->get_theme_icon(SNAME("ExternalLink"), SNAME("EditorIcons")), ED_SHORTCUT_AND_COMMAND("editor/q&a", TTR("Questions & Answers")), HELP_QA);
help_menu->add_icon_shortcut(gui_base->get_theme_icon(SNAME("ExternalLink"), SNAME("EditorIcons")), ED_SHORTCUT_AND_COMMAND("editor/report_a_bug", TTR("Report a Bug")), HELP_REPORT_A_BUG); help_menu->add_icon_shortcut(gui_base->get_theme_icon(SNAME("ExternalLink"), SNAME("EditorIcons")), ED_SHORTCUT_AND_COMMAND("editor/report_a_bug", TTR("Report a Bug")), HELP_REPORT_A_BUG);
help_menu->add_icon_shortcut(gui_base->get_theme_icon(SNAME("ActionCopy"), SNAME("EditorIcons")), ED_SHORTCUT_AND_COMMAND("editor/copy_system_info", TTR("Copy System Info")), HELP_COPY_SYSTEM_INFO);
help_menu->set_item_tooltip(-1, TTR("Copies the system info as a single-line text into the clipboard."));
help_menu->add_icon_shortcut(gui_base->get_theme_icon(SNAME("ExternalLink"), SNAME("EditorIcons")), ED_SHORTCUT_AND_COMMAND("editor/suggest_a_feature", TTR("Suggest a Feature")), HELP_SUGGEST_A_FEATURE); help_menu->add_icon_shortcut(gui_base->get_theme_icon(SNAME("ExternalLink"), SNAME("EditorIcons")), ED_SHORTCUT_AND_COMMAND("editor/suggest_a_feature", TTR("Suggest a Feature")), HELP_SUGGEST_A_FEATURE);
help_menu->add_icon_shortcut(gui_base->get_theme_icon(SNAME("ExternalLink"), SNAME("EditorIcons")), ED_SHORTCUT_AND_COMMAND("editor/send_docs_feedback", TTR("Send Docs Feedback")), HELP_SEND_DOCS_FEEDBACK); help_menu->add_icon_shortcut(gui_base->get_theme_icon(SNAME("ExternalLink"), SNAME("EditorIcons")), ED_SHORTCUT_AND_COMMAND("editor/send_docs_feedback", TTR("Send Docs Feedback")), HELP_SEND_DOCS_FEEDBACK);
help_menu->add_icon_shortcut(gui_base->get_theme_icon(SNAME("ExternalLink"), SNAME("EditorIcons")), ED_SHORTCUT_AND_COMMAND("editor/community", TTR("Community")), HELP_COMMUNITY); help_menu->add_icon_shortcut(gui_base->get_theme_icon(SNAME("ExternalLink"), SNAME("EditorIcons")), ED_SHORTCUT_AND_COMMAND("editor/community", TTR("Community")), HELP_COMMUNITY);
@ -7974,6 +8069,15 @@ EditorNode::EditorNode() {
raise_bottom_panel_item(AnimationPlayerEditor::get_singleton()); raise_bottom_panel_item(AnimationPlayerEditor::get_singleton());
add_editor_plugin(VersionControlEditorPlugin::get_singleton()); add_editor_plugin(VersionControlEditorPlugin::get_singleton());
vcs_actions_menu = VersionControlEditorPlugin::get_singleton()->get_version_control_actions_panel();
vcs_actions_menu->set_name("Version Control");
vcs_actions_menu->connect("index_pressed", callable_mp(this, &EditorNode::_version_control_menu_option));
vcs_actions_menu->add_item(TTR("Create Version Control Metadata"), RUN_VCS_METADATA);
vcs_actions_menu->add_item(TTR("Version Control Settings"), RUN_VCS_SETTINGS);
project_menu->add_child(vcs_actions_menu);
project_menu->set_item_submenu(project_menu->get_item_index(VCS_MENU), "Version Control");
add_editor_plugin(memnew(AudioBusesEditorPlugin(audio_bus_editor))); add_editor_plugin(memnew(AudioBusesEditorPlugin(audio_bus_editor)));
for (int i = 0; i < EditorPlugins::get_plugin_count(); i++) { for (int i = 0; i < EditorPlugins::get_plugin_count(); i++) {

@ -199,6 +199,7 @@ private:
RUN_USER_DATA_FOLDER, RUN_USER_DATA_FOLDER,
RELOAD_CURRENT_PROJECT, RELOAD_CURRENT_PROJECT,
RUN_PROJECT_MANAGER, RUN_PROJECT_MANAGER,
VCS_MENU,
RUN_VCS_METADATA, RUN_VCS_METADATA,
RUN_VCS_SETTINGS, RUN_VCS_SETTINGS,
SETTINGS_UPDATE_CONTINUOUSLY, SETTINGS_UPDATE_CONTINUOUSLY,
@ -229,6 +230,7 @@ private:
HELP_DOCS, HELP_DOCS,
HELP_QA, HELP_QA,
HELP_REPORT_A_BUG, HELP_REPORT_A_BUG,
HELP_COPY_SYSTEM_INFO,
HELP_SUGGEST_A_FEATURE, HELP_SUGGEST_A_FEATURE,
HELP_SEND_DOCS_FEEDBACK, HELP_SEND_DOCS_FEEDBACK,
HELP_COMMUNITY, HELP_COMMUNITY,
@ -521,6 +523,8 @@ private:
static int plugin_init_callback_count; static int plugin_init_callback_count;
static Vector<EditorNodeInitCallback> _init_callbacks; static Vector<EditorNodeInitCallback> _init_callbacks;
String _get_system_info() const;
static void _dependency_error_report(void *ud, const String &p_path, const String &p_dep, const String &p_type) { static void _dependency_error_report(void *ud, const String &p_path, const String &p_dep, const String &p_type) {
EditorNode *en = static_cast<EditorNode *>(ud); EditorNode *en = static_cast<EditorNode *>(ud);
if (!en->dependency_errors.has(p_path)) { if (!en->dependency_errors.has(p_path)) {

@ -278,7 +278,7 @@ void EditorPropertyArray::update_property() {
size_slider = memnew(EditorSpinSlider); size_slider = memnew(EditorSpinSlider);
size_slider->set_step(1); size_slider->set_step(1);
size_slider->set_max(1000000); size_slider->set_max(INT32_MAX);
size_slider->set_h_size_flags(SIZE_EXPAND_FILL); size_slider->set_h_size_flags(SIZE_EXPAND_FILL);
size_slider->set_read_only(is_read_only()); size_slider->set_read_only(is_read_only());
size_slider->connect("value_changed", callable_mp(this, &EditorPropertyArray::_length_changed)); size_slider->connect("value_changed", callable_mp(this, &EditorPropertyArray::_length_changed));

@ -469,6 +469,7 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) {
set_restart_if_changed("interface/touchscreen/enable_long_press_as_right_click", true); set_restart_if_changed("interface/touchscreen/enable_long_press_as_right_click", true);
EDITOR_SETTING(Variant::BOOL, PROPERTY_HINT_NONE, "interface/touchscreen/enable_pan_and_scale_gestures", has_touchscreen_ui, "") EDITOR_SETTING(Variant::BOOL, PROPERTY_HINT_NONE, "interface/touchscreen/enable_pan_and_scale_gestures", has_touchscreen_ui, "")
set_restart_if_changed("interface/touchscreen/enable_pan_and_scale_gestures", true); set_restart_if_changed("interface/touchscreen/enable_pan_and_scale_gestures", true);
EDITOR_SETTING(Variant::FLOAT, PROPERTY_HINT_RANGE, "interface/touchscreen/scale_gizmo_handles", has_touchscreen_ui ? 3 : 1, "1,5,1")
// Scene tabs // Scene tabs
EDITOR_SETTING(Variant::INT, PROPERTY_HINT_ENUM, "interface/scene_tabs/display_close_button", 1, "Never,If Tab Active,Always"); // TabBar::CloseButtonDisplayPolicy EDITOR_SETTING(Variant::INT, PROPERTY_HINT_ENUM, "interface/scene_tabs/display_close_button", 1, "Never,If Tab Active,Always"); // TabBar::CloseButtonDisplayPolicy
@ -688,7 +689,7 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) {
_initial_set("editors/tiles_editor/grid_color", Color(1.0, 0.5, 0.2, 0.5)); _initial_set("editors/tiles_editor/grid_color", Color(1.0, 0.5, 0.2, 0.5));
// Polygon editor // Polygon editor
_initial_set("editors/polygon_editor/point_grab_radius", 8); _initial_set("editors/polygon_editor/point_grab_radius", has_touchscreen_ui ? 32 : 8);
_initial_set("editors/polygon_editor/show_previous_outline", true); _initial_set("editors/polygon_editor/show_previous_outline", true);
// Animation // Animation

@ -255,6 +255,28 @@ static Ref<ImageTexture> editor_generate_icon(int p_index, float p_scale, float
return ImageTexture::create_from_image(img); return ImageTexture::create_from_image(img);
} }
float get_gizmo_handle_scale(const String &gizmo_handle_name = "") {
const float scale_gizmo_handles_for_touch = EDITOR_GET("interface/touchscreen/scale_gizmo_handles");
if (scale_gizmo_handles_for_touch > 1.0f) {
// The names of the icons that require additional scaling.
static HashSet<StringName> gizmo_to_scale;
if (gizmo_to_scale.is_empty()) {
gizmo_to_scale.insert("EditorHandle");
gizmo_to_scale.insert("EditorHandleAdd");
gizmo_to_scale.insert("EditorHandleDisabled");
gizmo_to_scale.insert("EditorCurveHandle");
gizmo_to_scale.insert("EditorPathSharpHandle");
gizmo_to_scale.insert("EditorPathSmoothHandle");
}
if (gizmo_to_scale.has(gizmo_handle_name)) {
return EDSCALE * scale_gizmo_handles_for_touch;
}
}
return EDSCALE;
}
void editor_register_and_generate_icons(Ref<Theme> p_theme, bool p_dark_theme, float p_icon_saturation, int p_thumb_size, bool p_only_thumbs = false) { void editor_register_and_generate_icons(Ref<Theme> p_theme, bool p_dark_theme, float p_icon_saturation, int p_thumb_size, bool p_only_thumbs = false) {
// Before we register the icons, we adjust their colors and saturation. // Before we register the icons, we adjust their colors and saturation.
// Most icons follow the standard rules for color conversion to follow the editor // Most icons follow the standard rules for color conversion to follow the editor
@ -314,22 +336,23 @@ void editor_register_and_generate_icons(Ref<Theme> p_theme, bool p_dark_theme, f
for (int i = 0; i < editor_icons_count; i++) { for (int i = 0; i < editor_icons_count; i++) {
Ref<ImageTexture> icon; Ref<ImageTexture> icon;
if (accent_color_icons.has(editor_icons_names[i])) { const String &editor_icon_name = editor_icons_names[i];
icon = editor_generate_icon(i, EDSCALE, 1.0, accent_color_map); if (accent_color_icons.has(editor_icon_name)) {
icon = editor_generate_icon(i, get_gizmo_handle_scale(editor_icon_name), 1.0, accent_color_map);
} else { } else {
float saturation = p_icon_saturation; float saturation = p_icon_saturation;
if (saturation_exceptions.has(editor_icons_names[i])) { if (saturation_exceptions.has(editor_icon_name)) {
saturation = 1.0; saturation = 1.0;
} }
if (conversion_exceptions.has(editor_icons_names[i])) { if (conversion_exceptions.has(editor_icon_name)) {
icon = editor_generate_icon(i, EDSCALE, saturation); icon = editor_generate_icon(i, get_gizmo_handle_scale(editor_icon_name), saturation);
} else { } else {
icon = editor_generate_icon(i, EDSCALE, saturation, color_conversion_map); icon = editor_generate_icon(i, get_gizmo_handle_scale(editor_icon_name), saturation, color_conversion_map);
} }
} }
p_theme->set_icon(editor_icons_names[i], SNAME("EditorIcons"), icon); p_theme->set_icon(editor_icon_name, SNAME("EditorIcons"), icon);
} }
} }
@ -395,6 +418,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
Color base_color = EDITOR_GET("interface/theme/base_color"); Color base_color = EDITOR_GET("interface/theme/base_color");
float contrast = EDITOR_GET("interface/theme/contrast"); float contrast = EDITOR_GET("interface/theme/contrast");
bool increase_scrollbar_touch_area = EDITOR_GET("interface/touchscreen/increase_scrollbar_touch_area"); bool increase_scrollbar_touch_area = EDITOR_GET("interface/touchscreen/increase_scrollbar_touch_area");
const float gizmo_handle_scale = EDITOR_GET("interface/touchscreen/scale_gizmo_handles");
bool draw_extra_borders = EDITOR_GET("interface/theme/draw_extra_borders"); bool draw_extra_borders = EDITOR_GET("interface/theme/draw_extra_borders");
float icon_saturation = EDITOR_GET("interface/theme/icon_saturation"); float icon_saturation = EDITOR_GET("interface/theme/icon_saturation");
float relationship_line_opacity = EDITOR_GET("interface/theme/relationship_line_opacity"); float relationship_line_opacity = EDITOR_GET("interface/theme/relationship_line_opacity");
@ -592,6 +616,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
theme->set_constant("thumb_size", "Editor", thumb_size); theme->set_constant("thumb_size", "Editor", thumb_size);
theme->set_constant("dark_theme", "Editor", dark_theme); theme->set_constant("dark_theme", "Editor", dark_theme);
theme->set_constant("color_picker_button_height", "Editor", 28 * EDSCALE); theme->set_constant("color_picker_button_height", "Editor", 28 * EDSCALE);
theme->set_constant("gizmo_handle_scale", "Editor", gizmo_handle_scale);
// Register editor icons. // Register editor icons.
// If the settings are comparable to the old theme, then just copy them over. // If the settings are comparable to the old theme, then just copy them over.
@ -607,8 +632,10 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
const bool prev_dark_theme = (bool)p_theme->get_constant(SNAME("dark_theme"), SNAME("Editor")); const bool prev_dark_theme = (bool)p_theme->get_constant(SNAME("dark_theme"), SNAME("Editor"));
const Color prev_accent_color = p_theme->get_color(SNAME("accent_color"), SNAME("Editor")); const Color prev_accent_color = p_theme->get_color(SNAME("accent_color"), SNAME("Editor"));
const float prev_icon_saturation = p_theme->get_color(SNAME("icon_saturation"), SNAME("Editor")).r; const float prev_icon_saturation = p_theme->get_color(SNAME("icon_saturation"), SNAME("Editor")).r;
const float prev_gizmo_handle_scale = (float)p_theme->get_constant(SNAME("gizmo_handle_scale"), SNAME("Editor"));
keep_old_icons = (Math::is_equal_approx(prev_scale, EDSCALE) && keep_old_icons = (Math::is_equal_approx(prev_scale, EDSCALE) &&
Math::is_equal_approx(prev_gizmo_handle_scale, gizmo_handle_scale) &&
prev_dark_theme == dark_theme && prev_dark_theme == dark_theme &&
prev_accent_color == accent_color && prev_accent_color == accent_color &&
prev_icon_saturation == icon_saturation); prev_icon_saturation == icon_saturation);

@ -434,6 +434,8 @@ void FileSystemDock::_notification(int p_what) {
} break; } break;
case NOTIFICATION_THEME_CHANGED: { case NOTIFICATION_THEME_CHANGED: {
overwrite_dialog_scroll->add_theme_style_override("panel", get_theme_stylebox("panel", "Tree"));
if (is_visible_in_tree()) { if (is_visible_in_tree()) {
_update_display_mode(true); _update_display_mode(true);
} }
@ -1686,10 +1688,10 @@ void FileSystemDock::_move_operation_confirm(const String &p_to_path, bool p_ove
Vector<String> conflicting_items = _check_existing(); Vector<String> conflicting_items = _check_existing();
if (!conflicting_items.is_empty()) { if (!conflicting_items.is_empty()) {
// Ask to do something. // Ask to do something.
overwrite_dialog->set_text(vformat( overwrite_dialog_header->set_text(vformat(
TTR("The following files or folders conflict with items in the target location '%s':\n\n%s\n\nDo you wish to overwrite them?"), TTR("The following files or folders conflict with items in the target location '%s':"), to_move_path));
to_move_path, overwrite_dialog_file_list->set_text(String("\n").join(conflicting_items));
String("\n").join(conflicting_items))); overwrite_dialog_footer->set_text(TTR("Do you wish to overwrite them?"));
overwrite_dialog->popup_centered(); overwrite_dialog->popup_centered();
return; return;
} }
@ -3298,6 +3300,22 @@ FileSystemDock::FileSystemDock() {
add_child(overwrite_dialog); add_child(overwrite_dialog);
overwrite_dialog->connect("confirmed", callable_mp(this, &FileSystemDock::_move_with_overwrite)); overwrite_dialog->connect("confirmed", callable_mp(this, &FileSystemDock::_move_with_overwrite));
VBoxContainer *overwrite_dialog_vb = memnew(VBoxContainer);
overwrite_dialog->add_child(overwrite_dialog_vb);
overwrite_dialog_header = memnew(Label);
overwrite_dialog_vb->add_child(overwrite_dialog_header);
overwrite_dialog_scroll = memnew(ScrollContainer);
overwrite_dialog_vb->add_child(overwrite_dialog_scroll);
overwrite_dialog_scroll->set_custom_minimum_size(Vector2(400, 600) * EDSCALE);
overwrite_dialog_file_list = memnew(Label);
overwrite_dialog_scroll->add_child(overwrite_dialog_file_list);
overwrite_dialog_footer = memnew(Label);
overwrite_dialog_vb->add_child(overwrite_dialog_footer);
duplicate_dialog = memnew(ConfirmationDialog); duplicate_dialog = memnew(ConfirmationDialog);
VBoxContainer *duplicate_dialog_vb = memnew(VBoxContainer); VBoxContainer *duplicate_dialog_vb = memnew(VBoxContainer);
duplicate_dialog->add_child(duplicate_dialog_vb); duplicate_dialog->add_child(duplicate_dialog_vb);

@ -151,7 +151,13 @@ private:
LineEdit *duplicate_dialog_text = nullptr; LineEdit *duplicate_dialog_text = nullptr;
ConfirmationDialog *make_dir_dialog = nullptr; ConfirmationDialog *make_dir_dialog = nullptr;
LineEdit *make_dir_dialog_text = nullptr; LineEdit *make_dir_dialog_text = nullptr;
ConfirmationDialog *overwrite_dialog = nullptr; ConfirmationDialog *overwrite_dialog = nullptr;
ScrollContainer *overwrite_dialog_scroll = nullptr;
Label *overwrite_dialog_header = nullptr;
Label *overwrite_dialog_footer = nullptr;
Label *overwrite_dialog_file_list = nullptr;
SceneCreateDialog *make_scene_dialog = nullptr; SceneCreateDialog *make_scene_dialog = nullptr;
ScriptCreateDialog *make_script_dialog = nullptr; ScriptCreateDialog *make_script_dialog = nullptr;
ShaderCreateDialog *make_shader_dialog = nullptr; ShaderCreateDialog *make_shader_dialog = nullptr;

@ -2413,7 +2413,7 @@ bool CanvasItemEditor::_gui_input_select(const Ref<InputEvent> &p_event) {
} }
} }
if (k.is_valid() && k->is_pressed() && k->get_keycode() == Key::ESCAPE && drag_type == DRAG_NONE && tool == TOOL_SELECT) { if (k.is_valid() && k->is_action_pressed(SNAME("ui_cancel"), false, true) && drag_type == DRAG_NONE && tool == TOOL_SELECT) {
// Unselect everything // Unselect everything
editor_selection->clear(); editor_selection->clear();
viewport->queue_redraw(); viewport->queue_redraw();
@ -4589,7 +4589,11 @@ void CanvasItemEditor::_popup_callback(int p_op) {
} break; } break;
case SKELETON_MAKE_BONES: { case SKELETON_MAKE_BONES: {
HashMap<Node *, Object *> &selection = editor_selection->get_selection(); HashMap<Node *, Object *> &selection = editor_selection->get_selection();
Node *editor_root = EditorNode::get_singleton()->get_edited_scene()->get_tree()->get_edited_scene_root(); Node *editor_root = get_tree()->get_edited_scene_root();
if (!editor_root || selection.is_empty()) {
return;
}
undo_redo->create_action(TTR("Create Custom Bone2D(s) from Node(s)")); undo_redo->create_action(TTR("Create Custom Bone2D(s) from Node(s)"));
for (const KeyValue<Node *, Object *> &E : selection) { for (const KeyValue<Node *, Object *> &E : selection) {

@ -33,6 +33,7 @@
#include "canvas_item_editor_plugin.h" #include "canvas_item_editor_plugin.h"
#include "core/os/keyboard.h" #include "core/os/keyboard.h"
#include "editor/editor_node.h" #include "editor/editor_node.h"
#include "editor/editor_settings.h"
#include "editor/editor_undo_redo_manager.h" #include "editor/editor_undo_redo_manager.h"
#include "scene/resources/capsule_shape_2d.h" #include "scene/resources/capsule_shape_2d.h"
#include "scene/resources/circle_shape_2d.h" #include "scene/resources/circle_shape_2d.h"
@ -44,6 +45,10 @@
#include "scene/resources/world_boundary_shape_2d.h" #include "scene/resources/world_boundary_shape_2d.h"
#include "scene/scene_string_names.h" #include "scene/scene_string_names.h"
CollisionShape2DEditor::CollisionShape2DEditor() {
grab_threshold = EDITOR_GET("editors/polygon_editor/point_grab_radius");
}
void CollisionShape2DEditor::_node_removed(Node *p_node) { void CollisionShape2DEditor::_node_removed(Node *p_node) {
if (p_node == node) { if (p_node == node) {
node = nullptr; node = nullptr;
@ -307,7 +312,7 @@ bool CollisionShape2DEditor::forward_canvas_gui_input(const Ref<InputEvent> &p_e
if (mb->get_button_index() == MouseButton::LEFT) { if (mb->get_button_index() == MouseButton::LEFT) {
if (mb->is_pressed()) { if (mb->is_pressed()) {
for (int i = 0; i < handles.size(); i++) { for (int i = 0; i < handles.size(); i++) {
if (xform.xform(handles[i]).distance_to(gpoint) < 8) { if (xform.xform(handles[i]).distance_to(gpoint) < grab_threshold) {
edit_handle = i; edit_handle = i;
break; break;
@ -529,6 +534,12 @@ void CollisionShape2DEditor::_notification(int p_what) {
_shape_changed(); _shape_changed();
} }
} break; } break;
case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {
if (EditorSettings::get_singleton()->check_changed_settings_in_group("editors/polygon_editor/point_grab_radius")) {
grab_threshold = EDITOR_GET("editors/polygon_editor/point_grab_radius");
}
} break;
} }
} }

@ -69,6 +69,7 @@ class CollisionShape2DEditor : public Control {
int shape_type = -1; int shape_type = -1;
int edit_handle = -1; int edit_handle = -1;
bool pressed = false; bool pressed = false;
real_t grab_threshold = 8;
Variant original; Variant original;
Transform2D original_transform; Transform2D original_transform;
Vector2 original_point; Vector2 original_point;
@ -90,6 +91,8 @@ public:
bool forward_canvas_gui_input(const Ref<InputEvent> &p_event); bool forward_canvas_gui_input(const Ref<InputEvent> &p_event);
void forward_canvas_draw_over_viewport(Control *p_overlay); void forward_canvas_draw_over_viewport(Control *p_overlay);
void edit(Node *p_node); void edit(Node *p_node);
CollisionShape2DEditor();
}; };
class CollisionShape2DEditorPlugin : public EditorPlugin { class CollisionShape2DEditorPlugin : public EditorPlugin {

@ -47,6 +47,7 @@ CurveEditor::CurveEditor() {
_tangents_length = 40; _tangents_length = 40;
_dragging = false; _dragging = false;
_has_undo_data = false; _has_undo_data = false;
_gizmo_handle_scale = EDITOR_GET("interface/touchscreen/scale_gizmo_handles");
set_focus_mode(FOCUS_ALL); set_focus_mode(FOCUS_ALL);
set_clip_contents(true); set_clip_contents(true);
@ -103,6 +104,11 @@ void CurveEditor::_notification(int p_what) {
case NOTIFICATION_DRAW: { case NOTIFICATION_DRAW: {
_draw(); _draw();
} break; } break;
case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {
if (EditorSettings::get_singleton()->check_changed_settings_in_group("interface/touchscreen/scale_gizmo_handles")) {
_gizmo_handle_scale = EDITOR_GET("interface/touchscreen/scale_gizmo_handles");
}
} break;
} }
} }
@ -395,7 +401,7 @@ int CurveEditor::get_point_at(Vector2 pos) const {
} }
const Curve &curve = **_curve_ref; const Curve &curve = **_curve_ref;
const float true_hover_radius = Math::round(_hover_radius * EDSCALE); const float true_hover_radius = Math::round(_hover_radius * _gizmo_handle_scale * EDSCALE);
const float r = true_hover_radius * true_hover_radius; const float r = true_hover_radius * true_hover_radius;
for (int i = 0; i < curve.get_point_count(); ++i) { for (int i = 0; i < curve.get_point_count(); ++i) {
@ -415,14 +421,14 @@ CurveEditor::TangentIndex CurveEditor::get_tangent_at(Vector2 pos) const {
if (_selected_point != 0) { if (_selected_point != 0) {
Vector2 control_pos = get_tangent_view_pos(_selected_point, TANGENT_LEFT); Vector2 control_pos = get_tangent_view_pos(_selected_point, TANGENT_LEFT);
if (control_pos.distance_to(pos) < _hover_radius) { if (control_pos.distance_to(pos) < _hover_radius * _gizmo_handle_scale) {
return TANGENT_LEFT; return TANGENT_LEFT;
} }
} }
if (_selected_point != _curve_ref->get_point_count() - 1) { if (_selected_point != _curve_ref->get_point_count() - 1) {
Vector2 control_pos = get_tangent_view_pos(_selected_point, TANGENT_RIGHT); Vector2 control_pos = get_tangent_view_pos(_selected_point, TANGENT_RIGHT);
if (control_pos.distance_to(pos) < _hover_radius) { if (control_pos.distance_to(pos) < _hover_radius * _gizmo_handle_scale) {
return TANGENT_RIGHT; return TANGENT_RIGHT;
} }
} }
@ -560,7 +566,7 @@ Vector2 CurveEditor::get_tangent_view_pos(int i, TangentIndex tangent) const {
Vector2 point_pos = get_view_pos(_curve_ref->get_point_position(i)); Vector2 point_pos = get_view_pos(_curve_ref->get_point_position(i));
Vector2 control_pos = get_view_pos(_curve_ref->get_point_position(i) + dir); Vector2 control_pos = get_view_pos(_curve_ref->get_point_position(i) + dir);
return point_pos + Math::round(_tangents_length * EDSCALE) * (control_pos - point_pos).normalized(); return point_pos + Math::round(_tangents_length * _gizmo_handle_scale * EDSCALE) * (control_pos - point_pos).normalized();
} }
Vector2 CurveEditor::get_view_pos(Vector2 world_pos) const { Vector2 CurveEditor::get_view_pos(Vector2 world_pos) const {
@ -705,13 +711,13 @@ void CurveEditor::_draw() {
if (i != 0) { if (i != 0) {
Vector2 control_pos = get_tangent_view_pos(i, TANGENT_LEFT); Vector2 control_pos = get_tangent_view_pos(i, TANGENT_LEFT);
draw_line(get_view_pos(pos), control_pos, tangent_color, Math::round(EDSCALE)); draw_line(get_view_pos(pos), control_pos, tangent_color, Math::round(EDSCALE));
draw_rect(Rect2(control_pos, Vector2(1, 1)).grow(Math::round(2 * EDSCALE)), tangent_color); draw_rect(Rect2(control_pos, Vector2(1, 1)).grow(Math::round(2 * _gizmo_handle_scale * EDSCALE)), tangent_color);
} }
if (i != curve.get_point_count() - 1) { if (i != curve.get_point_count() - 1) {
Vector2 control_pos = get_tangent_view_pos(i, TANGENT_RIGHT); Vector2 control_pos = get_tangent_view_pos(i, TANGENT_RIGHT);
draw_line(get_view_pos(pos), control_pos, tangent_color, Math::round(EDSCALE)); draw_line(get_view_pos(pos), control_pos, tangent_color, Math::round(EDSCALE));
draw_rect(Rect2(control_pos, Vector2(1, 1)).grow(Math::round(2 * EDSCALE)), tangent_color); draw_rect(Rect2(control_pos, Vector2(1, 1)).grow(Math::round(2 * _gizmo_handle_scale * EDSCALE)), tangent_color);
} }
} }
@ -734,7 +740,7 @@ void CurveEditor::_draw() {
for (int i = 0; i < curve.get_point_count(); ++i) { for (int i = 0; i < curve.get_point_count(); ++i) {
Vector2 pos = curve.get_point_position(i); Vector2 pos = curve.get_point_position(i);
draw_rect(Rect2(get_view_pos(pos), Vector2(1, 1)).grow(Math::round(3 * EDSCALE)), i == _selected_point ? selected_point_color : point_color); draw_rect(Rect2(get_view_pos(pos), Vector2(1, 1)).grow(Math::round(3 * _gizmo_handle_scale * EDSCALE)), i == _selected_point ? selected_point_color : point_color);
// TODO Circles are prettier. Needs a fix! Or a texture // TODO Circles are prettier. Needs a fix! Or a texture
//draw_circle(pos, 2, point_color); //draw_circle(pos, 2, point_color);
} }
@ -744,7 +750,7 @@ void CurveEditor::_draw() {
if (_hover_point != -1) { if (_hover_point != -1) {
const Color hover_color = line_color; const Color hover_color = line_color;
Vector2 pos = curve.get_point_position(_hover_point); Vector2 pos = curve.get_point_position(_hover_point);
draw_rect(Rect2(get_view_pos(pos), Vector2(1, 1)).grow(Math::round(_hover_radius * EDSCALE)), hover_color, false, Math::round(EDSCALE)); draw_rect(Rect2(get_view_pos(pos), Vector2(1, 1)).grow(Math::round(_hover_radius * _gizmo_handle_scale * EDSCALE)), hover_color, false, Math::round(EDSCALE));
} }
// Help text // Help text

@ -115,6 +115,7 @@ private:
// Constant // Constant
float _hover_radius; float _hover_radius;
float _tangents_length; float _tangents_length;
float _gizmo_handle_scale = 1.0;
}; };
class EditorInspectorPluginCurve : public EditorInspectorPlugin { class EditorInspectorPluginCurve : public EditorInspectorPlugin {

@ -1735,6 +1735,7 @@ SpriteFramesEditor::SpriteFramesEditor() {
frame_list = memnew(ItemList); frame_list = memnew(ItemList);
frame_list->set_v_size_flags(SIZE_EXPAND_FILL); frame_list->set_v_size_flags(SIZE_EXPAND_FILL);
frame_list->set_icon_mode(ItemList::ICON_MODE_TOP); frame_list->set_icon_mode(ItemList::ICON_MODE_TOP);
frame_list->set_texture_filter(TEXTURE_FILTER_NEAREST_WITH_MIPMAPS);
frame_list->set_max_columns(0); frame_list->set_max_columns(0);
frame_list->set_icon_mode(ItemList::ICON_MODE_TOP); frame_list->set_icon_mode(ItemList::ICON_MODE_TOP);
@ -1883,6 +1884,7 @@ SpriteFramesEditor::SpriteFramesEditor() {
split_sheet_preview = memnew(TextureRect); split_sheet_preview = memnew(TextureRect);
split_sheet_preview->set_expand_mode(TextureRect::EXPAND_IGNORE_SIZE); split_sheet_preview->set_expand_mode(TextureRect::EXPAND_IGNORE_SIZE);
split_sheet_preview->set_texture_filter(TEXTURE_FILTER_NEAREST_WITH_MIPMAPS);
split_sheet_preview->set_mouse_filter(MOUSE_FILTER_PASS); split_sheet_preview->set_mouse_filter(MOUSE_FILTER_PASS);
split_sheet_preview->connect("draw", callable_mp(this, &SpriteFramesEditor::_sheet_preview_draw)); split_sheet_preview->connect("draw", callable_mp(this, &SpriteFramesEditor::_sheet_preview_draw));
split_sheet_preview->connect("gui_input", callable_mp(this, &SpriteFramesEditor::_sheet_preview_input)); split_sheet_preview->connect("gui_input", callable_mp(this, &SpriteFramesEditor::_sheet_preview_input));

@ -1827,19 +1827,13 @@ void ThemeItemEditorDialog::_edit_theme_item_gui_input(const Ref<InputEvent> &p_
return; return;
} }
switch (k->get_keycode()) { if (k->is_action_pressed(SNAME("ui_accept"), false, true)) {
case Key::KP_ENTER: _confirm_edit_theme_item();
case Key::ENTER: { edit_theme_item_dialog->hide();
_confirm_edit_theme_item(); edit_theme_item_dialog->set_input_as_handled();
edit_theme_item_dialog->hide(); } else if (k->is_action_pressed(SNAME("ui_cancel"), false, true)) {
edit_theme_item_dialog->set_input_as_handled(); edit_theme_item_dialog->hide();
} break; edit_theme_item_dialog->set_input_as_handled();
case Key::ESCAPE: {
edit_theme_item_dialog->hide();
edit_theme_item_dialog->set_input_as_handled();
} break;
default:
break;
} }
} }
} }

@ -106,10 +106,11 @@ void AtlasMergingDialog::_generate_merged(Vector<Ref<TileSetAtlasSource>> p_atla
// Create tiles and alternatives, then copy their properties. // Create tiles and alternatives, then copy their properties.
for (int alternative_index = 0; alternative_index < atlas_source->get_alternative_tiles_count(tile_mapping.key); alternative_index++) { for (int alternative_index = 0; alternative_index < atlas_source->get_alternative_tiles_count(tile_mapping.key); alternative_index++) {
int alternative_id = atlas_source->get_alternative_tile_id(tile_mapping.key, alternative_index); int alternative_id = atlas_source->get_alternative_tile_id(tile_mapping.key, alternative_index);
int changed_id = -1;
if (alternative_id == 0) { if (alternative_id == 0) {
merged->create_tile(tile_mapping.value, atlas_source->get_tile_size_in_atlas(tile_mapping.key)); merged->create_tile(tile_mapping.value, atlas_source->get_tile_size_in_atlas(tile_mapping.key));
} else { } else {
merged->create_alternative_tile(tile_mapping.value, alternative_index); changed_id = merged->create_alternative_tile(tile_mapping.value, alternative_index);
} }
// Copy the properties. // Copy the properties.
@ -117,7 +118,7 @@ void AtlasMergingDialog::_generate_merged(Vector<Ref<TileSetAtlasSource>> p_atla
List<PropertyInfo> properties; List<PropertyInfo> properties;
src_tile_data->get_property_list(&properties); src_tile_data->get_property_list(&properties);
TileData *dst_tile_data = merged->get_tile_data(tile_mapping.value, alternative_id); TileData *dst_tile_data = merged->get_tile_data(tile_mapping.value, changed_id == -1 ? alternative_id : changed_id);
for (PropertyInfo property : properties) { for (PropertyInfo property : properties) {
if (!(property.usage & PROPERTY_USAGE_STORAGE)) { if (!(property.usage & PROPERTY_USAGE_STORAGE)) {
continue; continue;
@ -296,7 +297,7 @@ AtlasMergingDialog::AtlasMergingDialog() {
atlas_merging_atlases_list->set_fixed_icon_size(Size2(60, 60) * EDSCALE); atlas_merging_atlases_list->set_fixed_icon_size(Size2(60, 60) * EDSCALE);
atlas_merging_atlases_list->set_h_size_flags(Control::SIZE_EXPAND_FILL); atlas_merging_atlases_list->set_h_size_flags(Control::SIZE_EXPAND_FILL);
atlas_merging_atlases_list->set_v_size_flags(Control::SIZE_EXPAND_FILL); atlas_merging_atlases_list->set_v_size_flags(Control::SIZE_EXPAND_FILL);
atlas_merging_atlases_list->set_texture_filter(CanvasItem::TEXTURE_FILTER_NEAREST); atlas_merging_atlases_list->set_texture_filter(CanvasItem::TEXTURE_FILTER_NEAREST_WITH_MIPMAPS);
atlas_merging_atlases_list->set_custom_minimum_size(Size2(100, 200)); atlas_merging_atlases_list->set_custom_minimum_size(Size2(100, 200));
atlas_merging_atlases_list->set_select_mode(ItemList::SELECT_MULTI); atlas_merging_atlases_list->set_select_mode(ItemList::SELECT_MULTI);
atlas_merging_atlases_list->connect("multi_selected", callable_mp(this, &AtlasMergingDialog::_update_texture).unbind(2)); atlas_merging_atlases_list->connect("multi_selected", callable_mp(this, &AtlasMergingDialog::_update_texture).unbind(2));
@ -304,6 +305,7 @@ AtlasMergingDialog::AtlasMergingDialog() {
VBoxContainer *atlas_merging_right_panel = memnew(VBoxContainer); VBoxContainer *atlas_merging_right_panel = memnew(VBoxContainer);
atlas_merging_right_panel->set_h_size_flags(Control::SIZE_EXPAND_FILL); atlas_merging_right_panel->set_h_size_flags(Control::SIZE_EXPAND_FILL);
atlas_merging_right_panel->set_texture_filter(CanvasItem::TEXTURE_FILTER_NEAREST_WITH_MIPMAPS);
atlas_merging_h_split_container->add_child(atlas_merging_right_panel); atlas_merging_h_split_container->add_child(atlas_merging_right_panel);
// Settings. // Settings.

@ -942,7 +942,7 @@ VersionControlEditorPlugin::VersionControlEditorPlugin() {
metadata_dialog->set_title(TTR("Create Version Control Metadata")); metadata_dialog->set_title(TTR("Create Version Control Metadata"));
metadata_dialog->set_min_size(Size2(200, 40)); metadata_dialog->set_min_size(Size2(200, 40));
metadata_dialog->get_ok_button()->connect(SNAME("pressed"), callable_mp(this, &VersionControlEditorPlugin::_create_vcs_metadata_files)); metadata_dialog->get_ok_button()->connect(SNAME("pressed"), callable_mp(this, &VersionControlEditorPlugin::_create_vcs_metadata_files));
add_child(metadata_dialog); get_editor_interface()->get_base_control()->add_child(metadata_dialog);
VBoxContainer *metadata_vb = memnew(VBoxContainer); VBoxContainer *metadata_vb = memnew(VBoxContainer);
metadata_dialog->add_child(metadata_vb); metadata_dialog->add_child(metadata_vb);
@ -971,7 +971,7 @@ VersionControlEditorPlugin::VersionControlEditorPlugin() {
set_up_dialog->set_min_size(Size2(600, 100)); set_up_dialog->set_min_size(Size2(600, 100));
set_up_dialog->add_cancel_button("Cancel"); set_up_dialog->add_cancel_button("Cancel");
set_up_dialog->set_hide_on_ok(true); set_up_dialog->set_hide_on_ok(true);
add_child(set_up_dialog); get_editor_interface()->get_base_control()->add_child(set_up_dialog);
Button *set_up_apply_button = set_up_dialog->get_ok_button(); Button *set_up_apply_button = set_up_dialog->get_ok_button();
set_up_apply_button->set_text(TTR("Apply")); set_up_apply_button->set_text(TTR("Apply"));

@ -390,7 +390,7 @@ bool ProjectConverter3To4::convert() {
rename_gdscript_functions(source_lines, reg_container, false); // Require to additional rename. rename_gdscript_functions(source_lines, reg_container, false); // Require to additional rename.
rename_common(RenamesMap3To4::project_settings_renames, reg_container.project_settings_regexes, source_lines); rename_common(RenamesMap3To4::project_settings_renames, reg_container.project_settings_regexes, source_lines);
rename_gdscript_keywords(source_lines, reg_container); rename_gdscript_keywords(source_lines, reg_container, false);
rename_common(RenamesMap3To4::gdscript_properties_renames, reg_container.gdscript_properties_regexes, source_lines); rename_common(RenamesMap3To4::gdscript_properties_renames, reg_container.gdscript_properties_regexes, source_lines);
rename_common(RenamesMap3To4::gdscript_signals_renames, reg_container.gdscript_signals_regexes, source_lines); rename_common(RenamesMap3To4::gdscript_signals_renames, reg_container.gdscript_signals_regexes, source_lines);
rename_common(RenamesMap3To4::shaders_renames, reg_container.shaders_regexes, source_lines); rename_common(RenamesMap3To4::shaders_renames, reg_container.shaders_regexes, source_lines);
@ -408,7 +408,7 @@ bool ProjectConverter3To4::convert() {
rename_gdscript_functions(source_lines, reg_container, true); // Require to do additional renames. rename_gdscript_functions(source_lines, reg_container, true); // Require to do additional renames.
rename_common(RenamesMap3To4::project_settings_renames, reg_container.project_settings_regexes, source_lines); rename_common(RenamesMap3To4::project_settings_renames, reg_container.project_settings_regexes, source_lines);
rename_gdscript_keywords(source_lines, reg_container); rename_gdscript_keywords(source_lines, reg_container, true);
rename_common(RenamesMap3To4::gdscript_properties_renames, reg_container.gdscript_properties_regexes, source_lines); rename_common(RenamesMap3To4::gdscript_properties_renames, reg_container.gdscript_properties_regexes, source_lines);
rename_common(RenamesMap3To4::gdscript_signals_renames, reg_container.gdscript_signals_regexes, source_lines); rename_common(RenamesMap3To4::gdscript_signals_renames, reg_container.gdscript_signals_regexes, source_lines);
rename_common(RenamesMap3To4::shaders_renames, reg_container.shaders_regexes, source_lines); rename_common(RenamesMap3To4::shaders_renames, reg_container.shaders_regexes, source_lines);
@ -572,7 +572,7 @@ bool ProjectConverter3To4::validate_conversion() {
changed_elements.append_array(check_for_rename_gdscript_functions(lines, reg_container, false)); changed_elements.append_array(check_for_rename_gdscript_functions(lines, reg_container, false));
changed_elements.append_array(check_for_rename_common(RenamesMap3To4::project_settings_renames, reg_container.project_settings_regexes, lines)); changed_elements.append_array(check_for_rename_common(RenamesMap3To4::project_settings_renames, reg_container.project_settings_regexes, lines));
changed_elements.append_array(check_for_rename_gdscript_keywords(lines, reg_container)); changed_elements.append_array(check_for_rename_gdscript_keywords(lines, reg_container, false));
changed_elements.append_array(check_for_rename_common(RenamesMap3To4::gdscript_properties_renames, reg_container.gdscript_properties_regexes, lines)); changed_elements.append_array(check_for_rename_common(RenamesMap3To4::gdscript_properties_renames, reg_container.gdscript_properties_regexes, lines));
changed_elements.append_array(check_for_rename_common(RenamesMap3To4::gdscript_signals_renames, reg_container.gdscript_signals_regexes, lines)); changed_elements.append_array(check_for_rename_common(RenamesMap3To4::gdscript_signals_renames, reg_container.gdscript_signals_regexes, lines));
changed_elements.append_array(check_for_rename_common(RenamesMap3To4::shaders_renames, reg_container.shaders_regexes, lines)); changed_elements.append_array(check_for_rename_common(RenamesMap3To4::shaders_renames, reg_container.shaders_regexes, lines));
@ -590,7 +590,7 @@ bool ProjectConverter3To4::validate_conversion() {
changed_elements.append_array(check_for_rename_gdscript_functions(lines, reg_container, true)); changed_elements.append_array(check_for_rename_gdscript_functions(lines, reg_container, true));
changed_elements.append_array(check_for_rename_common(RenamesMap3To4::project_settings_renames, reg_container.project_settings_regexes, lines)); changed_elements.append_array(check_for_rename_common(RenamesMap3To4::project_settings_renames, reg_container.project_settings_regexes, lines));
changed_elements.append_array(check_for_rename_gdscript_keywords(lines, reg_container)); changed_elements.append_array(check_for_rename_gdscript_keywords(lines, reg_container, true));
changed_elements.append_array(check_for_rename_common(RenamesMap3To4::gdscript_properties_renames, reg_container.gdscript_properties_regexes, lines)); changed_elements.append_array(check_for_rename_common(RenamesMap3To4::gdscript_properties_renames, reg_container.gdscript_properties_regexes, lines));
changed_elements.append_array(check_for_rename_common(RenamesMap3To4::gdscript_signals_renames, reg_container.gdscript_signals_regexes, lines)); changed_elements.append_array(check_for_rename_common(RenamesMap3To4::gdscript_signals_renames, reg_container.gdscript_signals_regexes, lines));
changed_elements.append_array(check_for_rename_common(RenamesMap3To4::shaders_renames, reg_container.shaders_regexes, lines)); changed_elements.append_array(check_for_rename_common(RenamesMap3To4::shaders_renames, reg_container.shaders_regexes, lines));
@ -863,22 +863,26 @@ bool ProjectConverter3To4::test_conversion(RegExContainer &reg_container) {
valid = valid && test_conversion_with_regex("Spatial.shader", "Spatial.shader", &ProjectConverter3To4::rename_classes, "classes", reg_container); valid = valid && test_conversion_with_regex("Spatial.shader", "Spatial.shader", &ProjectConverter3To4::rename_classes, "classes", reg_container);
valid = valid && test_conversion_with_regex("Spatial.other", "Node3D.other", &ProjectConverter3To4::rename_classes, "classes", reg_container); valid = valid && test_conversion_with_regex("Spatial.other", "Node3D.other", &ProjectConverter3To4::rename_classes, "classes", reg_container);
valid = valid && test_conversion_with_regex("\nonready", "\n@onready", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword", reg_container); valid = valid && test_conversion_gdscript_builtin("\nonready", "\n@onready", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword", reg_container, false);
valid = valid && test_conversion_with_regex("onready", "@onready", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword", reg_container); valid = valid && test_conversion_gdscript_builtin("onready", "@onready", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword", reg_container, false);
valid = valid && test_conversion_with_regex(" onready", " onready", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword", reg_container); valid = valid && test_conversion_gdscript_builtin(" onready", " onready", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword", reg_container, false);
valid = valid && test_conversion_with_regex("\nexport", "\n@export", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword", reg_container); valid = valid && test_conversion_gdscript_builtin("\nexport", "\n@export", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword", reg_container, false);
valid = valid && test_conversion_with_regex("\texport", "\t@export", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword", reg_container); valid = valid && test_conversion_gdscript_builtin("\texport", "\t@export", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword", reg_container, false);
valid = valid && test_conversion_with_regex("\texport_dialog", "\texport_dialog", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword", reg_container); valid = valid && test_conversion_gdscript_builtin("\texport_dialog", "\texport_dialog", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword", reg_container, false);
valid = valid && test_conversion_with_regex("export", "@export", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword", reg_container); valid = valid && test_conversion_gdscript_builtin("export", "@export", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword", reg_container, false);
valid = valid && test_conversion_with_regex(" export", " export", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword", reg_container); valid = valid && test_conversion_gdscript_builtin(" export", " export", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword", reg_container, false);
valid = valid && test_conversion_with_regex("\n\nremote func", "\n\n@rpc(\"any_peer\") func", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword", reg_container); valid = valid && test_conversion_gdscript_builtin("\n\nremote func", "\n\n@rpc(\"any_peer\") func", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword", reg_container, false);
valid = valid && test_conversion_with_regex("\n\nremotesync func", "\n\n@rpc(\"any_peer\", \"call_local\") func", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword", reg_container); valid = valid && test_conversion_gdscript_builtin("\n\nremote func", "\n\n@rpc(\\\"any_peer\\\") func", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword", reg_container, true);
valid = valid && test_conversion_with_regex("\n\nsync func", "\n\n@rpc(\"any_peer\", \"call_local\") func", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword", reg_container); valid = valid && test_conversion_gdscript_builtin("\n\nremotesync func", "\n\n@rpc(\"any_peer\", \"call_local\") func", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword", reg_container, false);
valid = valid && test_conversion_with_regex("\n\nslave func", "\n\n@rpc func", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword", reg_container); valid = valid && test_conversion_gdscript_builtin("\n\nremotesync func", "\n\n@rpc(\\\"any_peer\\\", \\\"call_local\\\") func", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword", reg_container, true);
valid = valid && test_conversion_with_regex("\n\npuppet func", "\n\n@rpc func", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword", reg_container); valid = valid && test_conversion_gdscript_builtin("\n\nsync func", "\n\n@rpc(\"any_peer\", \"call_local\") func", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword", reg_container, false);
valid = valid && test_conversion_with_regex("\n\npuppetsync func", "\n\n@rpc(\"call_local\") func", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword", reg_container); valid = valid && test_conversion_gdscript_builtin("\n\nsync func", "\n\n@rpc(\\\"any_peer\\\", \\\"call_local\\\") func", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword", reg_container, true);
valid = valid && test_conversion_with_regex("\n\nmaster func", "\n\nThe master and mastersync rpc behavior is not officially supported anymore. Try using another keyword or making custom logic using get_multiplayer().get_remote_sender_id()\n@rpc func", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword", reg_container); valid = valid && test_conversion_gdscript_builtin("\n\nslave func", "\n\n@rpc func", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword", reg_container, false);
valid = valid && test_conversion_with_regex("\n\nmastersync func", "\n\nThe master and mastersync rpc behavior is not officially supported anymore. Try using another keyword or making custom logic using get_multiplayer().get_remote_sender_id()\n@rpc(\"call_local\") func", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword", reg_container); valid = valid && test_conversion_gdscript_builtin("\n\npuppet func", "\n\n@rpc func", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword", reg_container, false);
valid = valid && test_conversion_gdscript_builtin("\n\npuppetsync func", "\n\n@rpc(\"call_local\") func", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword", reg_container, false);
valid = valid && test_conversion_gdscript_builtin("\n\npuppetsync func", "\n\n@rpc(\\\"call_local\\\") func", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword", reg_container, true);
valid = valid && test_conversion_gdscript_builtin("\n\nmaster func", "\n\nThe master and mastersync rpc behavior is not officially supported anymore. Try using another keyword or making custom logic using get_multiplayer().get_remote_sender_id()\n@rpc func", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword", reg_container, false);
valid = valid && test_conversion_gdscript_builtin("\n\nmastersync func", "\n\nThe master and mastersync rpc behavior is not officially supported anymore. Try using another keyword or making custom logic using get_multiplayer().get_remote_sender_id()\n@rpc(\"call_local\") func", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword", reg_container, false);
valid = valid && test_conversion_gdscript_builtin("var size: Vector2 = Vector2() setget set_function, get_function", "var size: Vector2 = Vector2(): get = get_function, set = set_function", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false); valid = valid && test_conversion_gdscript_builtin("var size: Vector2 = Vector2() setget set_function, get_function", "var size: Vector2 = Vector2(): get = get_function, set = set_function", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
valid = valid && test_conversion_gdscript_builtin("var size: Vector2 = Vector2() setget set_function, ", "var size: Vector2 = Vector2(): set = set_function", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false); valid = valid && test_conversion_gdscript_builtin("var size: Vector2 = Vector2() setget set_function, ", "var size: Vector2 = Vector2(): set = set_function", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
@ -2442,7 +2446,15 @@ Vector<String> ProjectConverter3To4::check_for_rename_csharp_attributes(Vector<S
return found_renames; return found_renames;
} }
void ProjectConverter3To4::rename_gdscript_keywords(Vector<SourceLine> &source_lines, const RegExContainer &reg_container) { _FORCE_INLINE_ static String builtin_escape(const String &p_str, bool p_builtin) {
if (p_builtin) {
return p_str.replace("\"", "\\\"");
} else {
return p_str;
}
}
void ProjectConverter3To4::rename_gdscript_keywords(Vector<SourceLine> &source_lines, const RegExContainer &reg_container, bool builtin) {
static String error_message = "The master and mastersync rpc behavior is not officially supported anymore. Try using another keyword or making custom logic using get_multiplayer().get_remote_sender_id()\n"; static String error_message = "The master and mastersync rpc behavior is not officially supported anymore. Try using another keyword or making custom logic using get_multiplayer().get_remote_sender_id()\n";
for (SourceLine &source_line : source_lines) { for (SourceLine &source_line : source_lines) {
@ -2462,13 +2474,13 @@ void ProjectConverter3To4::rename_gdscript_keywords(Vector<SourceLine> &source_l
line = reg_container.keyword_gdscript_onready.sub(line, "@onready", true); line = reg_container.keyword_gdscript_onready.sub(line, "@onready", true);
} }
if (line.contains("remote")) { if (line.contains("remote")) {
line = reg_container.keyword_gdscript_remote.sub(line, "@rpc(\"any_peer\") func", true); line = reg_container.keyword_gdscript_remote.sub(line, builtin_escape("@rpc(\"any_peer\") func", builtin), true);
} }
if (line.contains("remote")) { if (line.contains("remote")) {
line = reg_container.keyword_gdscript_remotesync.sub(line, "@rpc(\"any_peer\", \"call_local\") func", true); line = reg_container.keyword_gdscript_remotesync.sub(line, builtin_escape("@rpc(\"any_peer\", \"call_local\") func", builtin), true);
} }
if (line.contains("sync")) { if (line.contains("sync")) {
line = reg_container.keyword_gdscript_sync.sub(line, "@rpc(\"any_peer\", \"call_local\") func", true); line = reg_container.keyword_gdscript_sync.sub(line, builtin_escape("@rpc(\"any_peer\", \"call_local\") func", builtin), true);
} }
if (line.contains("slave")) { if (line.contains("slave")) {
line = reg_container.keyword_gdscript_slave.sub(line, "@rpc func", true); line = reg_container.keyword_gdscript_slave.sub(line, "@rpc func", true);
@ -2477,19 +2489,19 @@ void ProjectConverter3To4::rename_gdscript_keywords(Vector<SourceLine> &source_l
line = reg_container.keyword_gdscript_puppet.sub(line, "@rpc func", true); line = reg_container.keyword_gdscript_puppet.sub(line, "@rpc func", true);
} }
if (line.contains("puppet")) { if (line.contains("puppet")) {
line = reg_container.keyword_gdscript_puppetsync.sub(line, "@rpc(\"call_local\") func", true); line = reg_container.keyword_gdscript_puppetsync.sub(line, builtin_escape("@rpc(\"call_local\") func", builtin), true);
} }
if (line.contains("master")) { if (line.contains("master")) {
line = reg_container.keyword_gdscript_master.sub(line, error_message + "@rpc func", true); line = reg_container.keyword_gdscript_master.sub(line, error_message + "@rpc func", true);
} }
if (line.contains("master")) { if (line.contains("master")) {
line = reg_container.keyword_gdscript_mastersync.sub(line, error_message + "@rpc(\"call_local\") func", true); line = reg_container.keyword_gdscript_mastersync.sub(line, error_message + builtin_escape("@rpc(\"call_local\") func", builtin), true);
} }
} }
} }
} }
Vector<String> ProjectConverter3To4::check_for_rename_gdscript_keywords(Vector<String> &lines, const RegExContainer &reg_container) { Vector<String> ProjectConverter3To4::check_for_rename_gdscript_keywords(Vector<String> &lines, const RegExContainer &reg_container, bool builtin) {
Vector<String> found_renames; Vector<String> found_renames;
int current_line = 1; int current_line = 1;
@ -2531,25 +2543,25 @@ Vector<String> ProjectConverter3To4::check_for_rename_gdscript_keywords(Vector<S
if (line.contains("remote")) { if (line.contains("remote")) {
old = line; old = line;
line = reg_container.keyword_gdscript_remote.sub(line, "@rpc(\"any_peer\") func", true); line = reg_container.keyword_gdscript_remote.sub(line, builtin_escape("@rpc(\"any_peer\") func", builtin), true);
if (old != line) { if (old != line) {
found_renames.append(line_formatter(current_line, "remote func", "@rpc(\"any_peer\") func", line)); found_renames.append(line_formatter(current_line, "remote func", builtin_escape("@rpc(\"any_peer\") func", builtin), line));
} }
} }
if (line.contains("remote")) { if (line.contains("remote")) {
old = line; old = line;
line = reg_container.keyword_gdscript_remotesync.sub(line, "@rpc(\"any_peer\", \"call_local\")) func", true); line = reg_container.keyword_gdscript_remotesync.sub(line, builtin_escape("@rpc(\"any_peer\", \"call_local\")) func", builtin), true);
if (old != line) { if (old != line) {
found_renames.append(line_formatter(current_line, "remotesync func", "@rpc(\"any_peer\", \"call_local\")) func", line)); found_renames.append(line_formatter(current_line, "remotesync func", builtin_escape("@rpc(\"any_peer\", \"call_local\")) func", builtin), line));
} }
} }
if (line.contains("sync")) { if (line.contains("sync")) {
old = line; old = line;
line = reg_container.keyword_gdscript_sync.sub(line, "@rpc(\"any_peer\", \"call_local\")) func", true); line = reg_container.keyword_gdscript_sync.sub(line, builtin_escape("@rpc(\"any_peer\", \"call_local\")) func", builtin), true);
if (old != line) { if (old != line) {
found_renames.append(line_formatter(current_line, "sync func", "@rpc(\"any_peer\", \"call_local\")) func", line)); found_renames.append(line_formatter(current_line, "sync func", builtin_escape("@rpc(\"any_peer\", \"call_local\")) func", builtin), line));
} }
} }
@ -2571,9 +2583,9 @@ Vector<String> ProjectConverter3To4::check_for_rename_gdscript_keywords(Vector<S
if (line.contains("puppet")) { if (line.contains("puppet")) {
old = line; old = line;
line = reg_container.keyword_gdscript_puppetsync.sub(line, "@rpc(\"call_local\") func", true); line = reg_container.keyword_gdscript_puppetsync.sub(line, builtin_escape("@rpc(\"call_local\") func", builtin), true);
if (old != line) { if (old != line) {
found_renames.append(line_formatter(current_line, "puppetsync func", "@rpc(\"call_local\") func", line)); found_renames.append(line_formatter(current_line, "puppetsync func", builtin_escape("@rpc(\"call_local\") func", builtin), line));
} }
} }
@ -2587,9 +2599,9 @@ Vector<String> ProjectConverter3To4::check_for_rename_gdscript_keywords(Vector<S
if (line.contains("master")) { if (line.contains("master")) {
old = line; old = line;
line = reg_container.keyword_gdscript_master.sub(line, "@rpc(\"call_local\") func", true); line = reg_container.keyword_gdscript_master.sub(line, builtin_escape("@rpc(\"call_local\") func", builtin), true);
if (old != line) { if (old != line) {
found_renames.append(line_formatter(current_line, "mastersync func", "@rpc(\"call_local\") func", line)); found_renames.append(line_formatter(current_line, "mastersync func", builtin_escape("@rpc(\"call_local\") func", builtin), line));
} }
} }
} }

@ -90,8 +90,8 @@ class ProjectConverter3To4 {
void rename_csharp_attributes(Vector<SourceLine> &source_lines, const RegExContainer &reg_container); void rename_csharp_attributes(Vector<SourceLine> &source_lines, const RegExContainer &reg_container);
Vector<String> check_for_rename_csharp_attributes(Vector<String> &lines, const RegExContainer &reg_container); Vector<String> check_for_rename_csharp_attributes(Vector<String> &lines, const RegExContainer &reg_container);
void rename_gdscript_keywords(Vector<SourceLine> &source_lines, const RegExContainer &reg_container); void rename_gdscript_keywords(Vector<SourceLine> &r_source_lines, const RegExContainer &p_reg_container, bool p_builtin);
Vector<String> check_for_rename_gdscript_keywords(Vector<String> &lines, const RegExContainer &reg_container); Vector<String> check_for_rename_gdscript_keywords(Vector<String> &r_lines, const RegExContainer &p_reg_container, bool p_builtin);
void rename_input_map_scancode(Vector<SourceLine> &source_lines, const RegExContainer &reg_container); void rename_input_map_scancode(Vector<SourceLine> &source_lines, const RegExContainer &reg_container);
Vector<String> check_for_rename_input_map_scancode(Vector<String> &lines, const RegExContainer &reg_container); Vector<String> check_for_rename_input_map_scancode(Vector<String> &lines, const RegExContainer &reg_container);

@ -3,6 +3,6 @@ SKIP_LIST="./.*,./**/.*,./bin,./thirdparty,*.desktop,*.gen.*,*.po,*.pot,*.rc,./A
SKIP_LIST+="./core/input/gamecontrollerdb.txt,./core/string/locales.h,./editor/renames_map_3_to_4.cpp,./misc/scripts/codespell.sh," SKIP_LIST+="./core/input/gamecontrollerdb.txt,./core/string/locales.h,./editor/renames_map_3_to_4.cpp,./misc/scripts/codespell.sh,"
SKIP_LIST+="./platform/android/java/lib/src/com,./platform/web/node_modules,./platform/web/package-lock.json," SKIP_LIST+="./platform/android/java/lib/src/com,./platform/web/node_modules,./platform/web/package-lock.json,"
IGNORE_LIST="curvelinear,doubleclick,expct,findn,gird,hel,inout,lod,nd,numer,ot,te,vai" IGNORE_LIST="curvelinear,doubleclick,expct,findn,gird,hel,inout,lod,mis,nd,numer,ot,te,vai"
codespell -w -q 3 -S "${SKIP_LIST}" -L "${IGNORE_LIST}" --builtin "clear,rare,en-GB_to_en-US" codespell -w -q 3 -S "${SKIP_LIST}" -L "${IGNORE_LIST}" --builtin "clear,rare,en-GB_to_en-US"

@ -311,6 +311,14 @@ void GDScriptEditorTranslationParserPlugin::_extract_from_call(GDScriptParser::C
} }
} }
} }
if (p_call->callee && p_call->callee->type == GDScriptParser::Node::SUBSCRIPT) {
GDScriptParser::SubscriptNode *subscript_node = static_cast<GDScriptParser::SubscriptNode *>(p_call->callee);
if (subscript_node->base && subscript_node->base->type == GDScriptParser::Node::CALL) {
GDScriptParser::CallNode *call_node = static_cast<GDScriptParser::CallNode *>(subscript_node->base);
_extract_from_call(call_node);
}
}
} }
void GDScriptEditorTranslationParserPlugin::_extract_fd_literals(GDScriptParser::ExpressionNode *p_expression) { void GDScriptEditorTranslationParserPlugin::_extract_fd_literals(GDScriptParser::ExpressionNode *p_expression) {

@ -1532,7 +1532,10 @@ CSharpInstance *CSharpInstance::create_for_managed_type(Object *p_owner, CSharpS
instance->_reference_owner_unsafe(); instance->_reference_owner_unsafe();
} }
p_script->instances.insert(p_owner); {
MutexLock lock(CSharpLanguage::get_singleton()->get_script_instances_mutex());
p_script->instances.insert(p_owner);
}
return instance; return instance;
} }

@ -41,6 +41,12 @@ namespace GodotTools.Build
startInfo.EnvironmentVariables["DOTNET_CLI_UI_LANGUAGE"] startInfo.EnvironmentVariables["DOTNET_CLI_UI_LANGUAGE"]
= ((string)editorSettings.GetSetting("interface/editor/editor_language")).Replace('_', '-'); = ((string)editorSettings.GetSetting("interface/editor/editor_language")).Replace('_', '-');
if (OperatingSystem.IsWindows())
{
startInfo.StandardOutputEncoding = Encoding.UTF8;
startInfo.StandardErrorEncoding = Encoding.UTF8;
}
// Needed when running from Developer Command Prompt for VS // Needed when running from Developer Command Prompt for VS
RemovePlatformVariable(startInfo.EnvironmentVariables); RemovePlatformVariable(startInfo.EnvironmentVariables);
@ -105,6 +111,12 @@ namespace GodotTools.Build
startInfo.EnvironmentVariables["DOTNET_CLI_UI_LANGUAGE"] startInfo.EnvironmentVariables["DOTNET_CLI_UI_LANGUAGE"]
= ((string)editorSettings.GetSetting("interface/editor/editor_language")).Replace('_', '-'); = ((string)editorSettings.GetSetting("interface/editor/editor_language")).Replace('_', '-');
if (OperatingSystem.IsWindows())
{
startInfo.StandardOutputEncoding = Encoding.UTF8;
startInfo.StandardErrorEncoding = Encoding.UTF8;
}
// Needed when running from Developer Command Prompt for VS // Needed when running from Developer Command Prompt for VS
RemovePlatformVariable(startInfo.EnvironmentVariables); RemovePlatformVariable(startInfo.EnvironmentVariables);

@ -4,6 +4,7 @@ using System.Diagnostics;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using System.IO; using System.IO;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text;
using JetBrains.Annotations; using JetBrains.Annotations;
using OS = GodotTools.Utils.OS; using OS = GodotTools.Utils.OS;
@ -58,6 +59,11 @@ namespace GodotTools.Build
RedirectStandardOutput = true RedirectStandardOutput = true
}; };
if (OperatingSystem.IsWindows())
{
process.StartInfo.StandardOutputEncoding = Encoding.UTF8;
}
process.StartInfo.EnvironmentVariables["DOTNET_CLI_UI_LANGUAGE"] = "en-US"; process.StartInfo.EnvironmentVariables["DOTNET_CLI_UI_LANGUAGE"] = "en-US";
var lines = new List<string>(); var lines = new List<string>();

@ -103,7 +103,7 @@ namespace Godot.NativeInterop
{ {
try try
{ {
if (NativeFuncs.godotsharp_internal_script_debugger_is_active()) if (NativeFuncs.godotsharp_internal_script_debugger_is_active().ToBool())
{ {
SendToScriptDebugger(e); SendToScriptDebugger(e);
} }
@ -122,7 +122,7 @@ namespace Godot.NativeInterop
{ {
try try
{ {
if (NativeFuncs.godotsharp_internal_script_debugger_is_active()) if (NativeFuncs.godotsharp_internal_script_debugger_is_active().ToBool())
{ {
SendToScriptDebugger(e); SendToScriptDebugger(e);
} }

@ -56,7 +56,7 @@ namespace Godot.NativeInterop
in godot_string p_file, int p_line, in godot_string p_err, in godot_string p_descr, in godot_string p_file, int p_line, in godot_string p_err, in godot_string p_descr,
godot_bool p_warning, in DebuggingUtils.godot_stack_info_vector p_stack_info_vector); godot_bool p_warning, in DebuggingUtils.godot_stack_info_vector p_stack_info_vector);
internal static partial bool godotsharp_internal_script_debugger_is_active(); internal static partial godot_bool godotsharp_internal_script_debugger_is_active();
internal static partial IntPtr godotsharp_internal_object_get_associated_gchandle(IntPtr ptr); internal static partial IntPtr godotsharp_internal_object_get_associated_gchandle(IntPtr ptr);

@ -1 +0,0 @@
<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m2 1c-.55228 0-1 .44772-1 1v12c0 .55228.44772 1 1 1h12c.55228 0 1-.44772 1-1v-12c0-.55228-.44772-1-1-1zm1 2h10v8h-10zm3 1v2h2v-2zm2 2v2h2v2h2v-6h-2v2zm0 2h-2v-2h-2v4h4z" fill="#e0e0e0" fill-opacity=".99608"/></svg>

Before

Width:  |  Height:  |  Size: 307 B

@ -0,0 +1 @@
<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M2 1a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1V2a1 1 0 0 0-1-1zm1 2h10v8H3zm3 1v2h2V4zm2 2v2h2v2h2V4h-2v2zm0 2H6V6H4v4h4z" fill="#e0e0e0"/></svg>

After

Width:  |  Height:  |  Size: 244 B

@ -56,7 +56,10 @@ if lib_arch_dir != "":
if env.dev_build: if env.dev_build:
lib_type_dir = "dev" lib_type_dir = "dev"
elif env.debug_features: elif env.debug_features:
lib_type_dir = "debug" if env.editor_build and env["store_release"]:
lib_type_dir = "release"
else:
lib_type_dir = "debug"
else: # Release else: # Release
lib_type_dir = "release" lib_type_dir = "release"

@ -60,6 +60,7 @@ static AndroidGodotCodePair android_godot_code_pairs[] = {
{ AKEYCODE_DPAD_DOWN, Key::DOWN }, // (20) Directional Pad Down key. { AKEYCODE_DPAD_DOWN, Key::DOWN }, // (20) Directional Pad Down key.
{ AKEYCODE_DPAD_LEFT, Key::LEFT }, // (21) Directional Pad Left key. { AKEYCODE_DPAD_LEFT, Key::LEFT }, // (21) Directional Pad Left key.
{ AKEYCODE_DPAD_RIGHT, Key::RIGHT }, // (22) Directional Pad Right key. { AKEYCODE_DPAD_RIGHT, Key::RIGHT }, // (22) Directional Pad Right key.
{ AKEYCODE_DPAD_CENTER, Key::ENTER }, // (23) Directional Pad Center key.
{ AKEYCODE_VOLUME_UP, Key::VOLUMEUP }, // (24) Volume Up key. { AKEYCODE_VOLUME_UP, Key::VOLUMEUP }, // (24) Volume Up key.
{ AKEYCODE_VOLUME_DOWN, Key::VOLUMEDOWN }, // (25) Volume Down key. { AKEYCODE_VOLUME_DOWN, Key::VOLUMEDOWN }, // (25) Volume Down key.
{ AKEYCODE_POWER, Key::STANDBY }, // (26) Power key. { AKEYCODE_POWER, Key::STANDBY }, // (26) Power key.

@ -22,6 +22,8 @@ def can_build():
def get_opts(): def get_opts():
from SCons.Variables import BoolVariable
return [ return [
("ANDROID_SDK_ROOT", "Path to the Android SDK", get_env_android_sdk_root()), ("ANDROID_SDK_ROOT", "Path to the Android SDK", get_env_android_sdk_root()),
( (
@ -29,6 +31,7 @@ def get_opts():
'Target platform (android-<api>, e.g. "android-' + str(get_min_target_api()) + '")', 'Target platform (android-<api>, e.g. "android-' + str(get_min_target_api()) + '")',
"android-" + str(get_min_target_api()), "android-" + str(get_min_target_api()),
), ),
BoolVariable("store_release", "Editor build for Google Play Store (for official builds only)", False),
] ]

@ -135,14 +135,16 @@ ext.generateGodotLibraryVersion = { List<String> requiredKeys ->
String statusValue = map["status"] String statusValue = map["status"]
if (statusValue == null) { if (statusValue == null) {
statusCode = 0 statusCode = 0
} else if (statusValue.startsWith("alpha")) { } else if (statusValue.startsWith("dev")) {
statusCode = 1 statusCode = 1
} else if (statusValue.startsWith("beta")) { } else if (statusValue.startsWith("alpha")) {
statusCode = 2 statusCode = 2
} else if (statusValue.startsWith("rc")) { } else if (statusValue.startsWith("beta")) {
statusCode = 3 statusCode = 3
} else if (statusValue.startsWith("stable")) { } else if (statusValue.startsWith("rc")) {
statusCode = 4 statusCode = 4
} else if (statusValue.startsWith("stable")) {
statusCode = 5
} else { } else {
statusCode = 0 statusCode = 0
} }

@ -9,7 +9,7 @@ buildscript {
dependencies { dependencies {
classpath libraries.androidGradlePlugin classpath libraries.androidGradlePlugin
classpath libraries.kotlinGradlePlugin classpath libraries.kotlinGradlePlugin
classpath 'io.github.gradle-nexus:publish-plugin:1.1.0' classpath 'io.github.gradle-nexus:publish-plugin:1.3.0'
} }
} }
@ -38,9 +38,7 @@ ext {
supportedAbis = ["arm32", "arm64", "x86_32", "x86_64"] supportedAbis = ["arm32", "arm64", "x86_32", "x86_64"]
supportedFlavors = ["editor", "template"] supportedFlavors = ["editor", "template"]
supportedFlavorsBuildTypes = [ supportedFlavorsBuildTypes = [
// The editor can't be used with target=release as debugging tools are then not "editor": ["dev", "debug", "release"],
// included, and it would crash on errors instead of reporting them.
"editor": ["dev", "debug"],
"template": ["dev", "debug", "release"] "template": ["dev", "debug", "release"]
] ]
@ -54,6 +52,7 @@ ext {
def rootDir = "../../.." def rootDir = "../../.."
def binDir = "$rootDir/bin/" def binDir = "$rootDir/bin/"
def androidEditorBuildsDir = "$binDir/android_editor_builds/"
def getSconsTaskName(String flavor, String buildType, String abi) { def getSconsTaskName(String flavor, String buildType, String abi) {
return "compileGodotNativeLibs" + flavor.capitalize() + buildType.capitalize() + abi.capitalize() return "compileGodotNativeLibs" + flavor.capitalize() + buildType.capitalize() + abi.capitalize()
@ -221,18 +220,46 @@ def isAndroidStudio() {
return sysProps != null && sysProps['idea.platform.prefix'] != null return sysProps != null && sysProps['idea.platform.prefix'] != null
} }
task copyEditorDebugBinaryToBin(type: Copy) { task copyEditorReleaseApkToBin(type: Copy) {
dependsOn ':editor:assembleRelease'
from('editor/build/outputs/apk/release')
into(androidEditorBuildsDir)
include('android_editor-release*.apk')
}
task copyEditorReleaseAabToBin(type: Copy) {
dependsOn ':editor:bundleRelease'
from('editor/build/outputs/bundle/release')
into(androidEditorBuildsDir)
include('android_editor-release*.aab')
}
task copyEditorDebugApkToBin(type: Copy) {
dependsOn ':editor:assembleDebug' dependsOn ':editor:assembleDebug'
from('editor/build/outputs/apk/debug') from('editor/build/outputs/apk/debug')
into(binDir) into(androidEditorBuildsDir)
include('android_editor.apk') include('android_editor-debug.apk')
} }
task copyEditorDevBinaryToBin(type: Copy) { task copyEditorDebugAabToBin(type: Copy) {
dependsOn ':editor:bundleDebug'
from('editor/build/outputs/bundle/debug')
into(androidEditorBuildsDir)
include('android_editor-debug.aab')
}
task copyEditorDevApkToBin(type: Copy) {
dependsOn ':editor:assembleDev' dependsOn ':editor:assembleDev'
from('editor/build/outputs/apk/dev') from('editor/build/outputs/apk/dev')
into(binDir) into(androidEditorBuildsDir)
include('android_editor_dev.apk') include('android_editor-dev.apk')
}
task copyEditorDevAabToBin(type: Copy) {
dependsOn ':editor:bundleDev'
from('editor/build/outputs/bundle/dev')
into(androidEditorBuildsDir)
include('android_editor-dev.aab')
} }
/** /**
@ -253,7 +280,8 @@ task generateGodotEditor {
&& targetLibs.isDirectory() && targetLibs.isDirectory()
&& targetLibs.listFiles() != null && targetLibs.listFiles() != null
&& targetLibs.listFiles().length > 0) { && targetLibs.listFiles().length > 0) {
tasks += "copyEditor${target.capitalize()}BinaryToBin" tasks += "copyEditor${target.capitalize()}ApkToBin"
tasks += "copyEditor${target.capitalize()}AabToBin"
} }
} }
@ -301,9 +329,11 @@ task cleanGodotEditor(type: Delete) {
// Delete the generated binary apks // Delete the generated binary apks
delete("editor/build/outputs/apk") delete("editor/build/outputs/apk")
// Delete the Godot editor apks in the Godot bin directory // Delete the generated aab binaries
delete("$binDir/android_editor.apk") delete("editor/build/outputs/bundle")
delete("$binDir/android_editor_dev.apk")
// Delete the Godot editor apks & aabs in the Godot bin directory
delete(androidEditorBuildsDir)
} }
/** /**

@ -13,22 +13,67 @@ dependencies {
} }
ext { ext {
// Build number added as a suffix to the version code, and incremented for each build/upload to // Retrieve the build number from the environment variable; default to 0 if none is specified.
// the Google Play store. // The build number is added as a suffix to the version code for upload to the Google Play store.
// This should be reset on each stable release of Godot. getEditorBuildNumber = { ->
editorBuildNumber = 0 int buildNumber = 0
String versionStatus = System.getenv("GODOT_VERSION_STATUS")
if (versionStatus != null && !versionStatus.isEmpty()) {
try {
buildNumber = Integer.parseInt(versionStatus.replaceAll("[^0-9]", ""));
} catch (NumberFormatException ignored) {
buildNumber = 0
}
}
return buildNumber
}
// Value by which the Godot version code should be offset by to make room for the build number // Value by which the Godot version code should be offset by to make room for the build number
editorBuildNumberOffset = 100 editorBuildNumberOffset = 100
// Return the keystore file used for signing the release build.
getGodotKeystoreFile = { ->
def keyStore = System.getenv("GODOT_ANDROID_SIGN_KEYSTORE")
if (keyStore == null) {
return null
}
return file(keyStore)
}
// Return the key alias used for signing the release build.
getGodotKeyAlias = { ->
def kAlias = System.getenv("GODOT_ANDROID_KEYSTORE_ALIAS")
return kAlias
}
// Return the password for the key used for signing the release build.
getGodotSigningPassword = { ->
def signingPassword = System.getenv("GODOT_ANDROID_SIGN_PASSWORD")
return signingPassword
}
// Returns true if the environment variables contains the configuration for signing the release
// build.
hasReleaseSigningConfigs = { ->
def keystoreFile = getGodotKeystoreFile()
def keyAlias = getGodotKeyAlias()
def signingPassword = getGodotSigningPassword()
return keystoreFile != null && keystoreFile.isFile()
&& keyAlias != null && !keyAlias.isEmpty()
&& signingPassword != null && !signingPassword.isEmpty()
}
} }
def generateVersionCode() { def generateVersionCode() {
int libraryVersionCode = getGodotLibraryVersionCode() int libraryVersionCode = getGodotLibraryVersionCode()
return (libraryVersionCode * editorBuildNumberOffset) + editorBuildNumber return (libraryVersionCode * editorBuildNumberOffset) + getEditorBuildNumber()
} }
def generateVersionName() { def generateVersionName() {
String libraryVersionName = getGodotLibraryVersionName() String libraryVersionName = getGodotLibraryVersionName()
return libraryVersionName + ".$editorBuildNumber" int buildNumber = getEditorBuildNumber()
return buildNumber == 0 ? libraryVersionName : libraryVersionName + ".$buildNumber"
} }
android { android {
@ -45,6 +90,7 @@ android {
targetSdkVersion versions.targetSdk targetSdkVersion versions.targetSdk
missingDimensionStrategy 'products', 'editor' missingDimensionStrategy 'products', 'editor'
setProperty("archivesBaseName", "android_editor")
} }
compileOptions { compileOptions {
@ -56,6 +102,15 @@ android {
jvmTarget = versions.javaVersion jvmTarget = versions.javaVersion
} }
signingConfigs {
release {
storeFile getGodotKeystoreFile()
storePassword getGodotSigningPassword()
keyAlias getGodotKeyAlias()
keyPassword getGodotSigningPassword()
}
}
buildTypes { buildTypes {
dev { dev {
initWith debug initWith debug
@ -64,15 +119,14 @@ android {
debug { debug {
initWith release initWith release
applicationIdSuffix ".debug"
// Need to swap with the release signing config when this is ready for public release.
signingConfig signingConfigs.debug signingConfig signingConfigs.debug
} }
release { release {
// This buildtype is disabled below. if (hasReleaseSigningConfigs()) {
// The editor can't be used with target=release only, as debugging tools are then not signingConfig signingConfigs.release
// included, and it would crash on errors instead of reporting them. }
} }
} }
@ -82,20 +136,4 @@ android {
doNotStrip '**/*.so' doNotStrip '**/*.so'
} }
} }
// Disable 'release' buildtype.
// The editor can't be used with target=release only, as debugging tools are then not
// included, and it would crash on errors instead of reporting them.
variantFilter { variant ->
if (variant.buildType.name == "release") {
setIgnore(true)
}
}
applicationVariants.all { variant ->
variant.outputs.all { output ->
def suffix = variant.name == "dev" ? "_dev" : ""
output.outputFileName = "android_editor${suffix}.apk"
}
}
} }

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="godot_editor_name_string">Godot Editor 4 (debug)</string>
</resources>

@ -80,19 +80,11 @@ android {
release.jniLibs.srcDirs = ['libs/release'] release.jniLibs.srcDirs = ['libs/release']
// Editor jni library // Editor jni library
editorRelease.jniLibs.srcDirs = ['libs/tools/release']
editorDebug.jniLibs.srcDirs = ['libs/tools/debug'] editorDebug.jniLibs.srcDirs = ['libs/tools/debug']
editorDev.jniLibs.srcDirs = ['libs/tools/dev'] editorDev.jniLibs.srcDirs = ['libs/tools/dev']
} }
// Disable 'editorRelease'.
// The editor can't be used with target=release as debugging tools are then not
// included, and it would crash on errors instead of reporting them.
variantFilter { variant ->
if (variant.name == "editorRelease") {
setIgnore(true)
}
}
libraryVariants.all { variant -> libraryVariants.all { variant ->
def flavorName = variant.getFlavorName() def flavorName = variant.getFlavorName()
if (flavorName == null || flavorName == "") { if (flavorName == null || flavorName == "") {
@ -105,9 +97,14 @@ android {
} }
boolean devBuild = buildType == "dev" boolean devBuild = buildType == "dev"
boolean runTests = devBuild
boolean productionBuild = !devBuild
boolean storeRelease = buildType == "release"
def sconsTarget = flavorName def sconsTarget = flavorName
if (sconsTarget == "template") { if (sconsTarget == "template") {
// Tests are not supported on template builds
runTests = false
switch (buildType) { switch (buildType) {
case "release": case "release":
sconsTarget += "_release" sconsTarget += "_release"
@ -135,10 +132,10 @@ android {
def sconsExts = (org.gradle.internal.os.OperatingSystem.current().isWindows() def sconsExts = (org.gradle.internal.os.OperatingSystem.current().isWindows()
? [".bat", ".cmd", ".ps1", ".exe"] ? [".bat", ".cmd", ".ps1", ".exe"]
: [""]) : [""])
logger.lifecycle("Looking for $sconsName executable path") logger.debug("Looking for $sconsName executable path")
for (ext in sconsExts) { for (ext in sconsExts) {
String sconsNameExt = sconsName + ext String sconsNameExt = sconsName + ext
logger.lifecycle("Checking $sconsNameExt") logger.debug("Checking $sconsNameExt")
sconsExecutableFile = org.gradle.internal.os.OperatingSystem.current().findInPath(sconsNameExt) sconsExecutableFile = org.gradle.internal.os.OperatingSystem.current().findInPath(sconsNameExt)
if (sconsExecutableFile != null) { if (sconsExecutableFile != null) {
// We're done! // We're done!
@ -155,7 +152,7 @@ android {
if (sconsExecutableFile == null) { if (sconsExecutableFile == null) {
throw new GradleException("Unable to find executable path for the '$sconsName' command.") throw new GradleException("Unable to find executable path for the '$sconsName' command.")
} else { } else {
logger.lifecycle("Found executable path for $sconsName: ${sconsExecutableFile.absolutePath}") logger.debug("Found executable path for $sconsName: ${sconsExecutableFile.absolutePath}")
} }
for (String selectedAbi : selectedAbis) { for (String selectedAbi : selectedAbis) {
@ -167,7 +164,7 @@ android {
def taskName = getSconsTaskName(flavorName, buildType, selectedAbi) def taskName = getSconsTaskName(flavorName, buildType, selectedAbi)
tasks.create(name: taskName, type: Exec) { tasks.create(name: taskName, type: Exec) {
executable sconsExecutableFile.absolutePath executable sconsExecutableFile.absolutePath
args "--directory=${pathToRootDir}", "platform=android", "dev_mode=${devBuild}", "dev_build=${devBuild}", "target=${sconsTarget}", "arch=${selectedAbi}", "-j" + Runtime.runtime.availableProcessors() args "--directory=${pathToRootDir}", "platform=android", "store_release=${storeRelease}", "production=${productionBuild}", "dev_mode=${devBuild}", "dev_build=${devBuild}", "tests=${runTests}", "target=${sconsTarget}", "arch=${selectedAbi}", "-j" + Runtime.runtime.availableProcessors()
} }
// Schedule the tasks so the generated libs are present before the aar file is packaged. // Schedule the tasks so the generated libs are present before the aar file is packaged.

@ -166,8 +166,10 @@ public class GodotGLRenderView extends GLSurfaceView implements GodotRenderView
@Override @Override
public void requestPointerCapture() { public void requestPointerCapture() {
super.requestPointerCapture(); if (canCapturePointer()) {
inputHandler.onPointerCaptureChange(true); super.requestPointerCapture();
inputHandler.onPointerCaptureChange(true);
}
} }
@Override @Override

@ -51,4 +51,8 @@ public interface GodotRenderView {
void configurePointerIcon(int pointerType, String imagePath, float hotSpotX, float hotSpotY); void configurePointerIcon(int pointerType, String imagePath, float hotSpotX, float hotSpotY);
void setPointerIcon(int pointerType); void setPointerIcon(int pointerType);
default boolean canCapturePointer() {
return getInputHandler().canCapturePointer();
}
} }

@ -134,8 +134,10 @@ public class GodotVulkanRenderView extends VkSurfaceView implements GodotRenderV
@Override @Override
public void requestPointerCapture() { public void requestPointerCapture() {
super.requestPointerCapture(); if (canCapturePointer()) {
mInputHandler.onPointerCaptureChange(true); super.requestPointerCapture();
mInputHandler.onPointerCaptureChange(true);
}
} }
@Override @Override

@ -227,35 +227,36 @@ internal class GodotGestureHandler : SimpleOnGestureListener(), OnScaleGestureLi
) )
dragInProgress = false dragInProgress = false
} }
return true
} }
dragInProgress = true
val x = terminusEvent.x val x = terminusEvent.x
val y = terminusEvent.y val y = terminusEvent.y
if (terminusEvent.pointerCount >= 2 && panningAndScalingEnabled && !pointerCaptureInProgress) { if (terminusEvent.pointerCount >= 2 && panningAndScalingEnabled && !pointerCaptureInProgress && !dragInProgress) {
GodotLib.pan(x, y, distanceX / 5f, distanceY / 5f) GodotLib.pan(x, y, distanceX / 5f, distanceY / 5f)
} else { } else if (!scaleInProgress){
dragInProgress = true
GodotInputHandler.handleMotionEvent(terminusEvent) GodotInputHandler.handleMotionEvent(terminusEvent)
} }
return true return true
} }
override fun onScale(detector: ScaleGestureDetector): Boolean { override fun onScale(detector: ScaleGestureDetector): Boolean {
if (!panningAndScalingEnabled || pointerCaptureInProgress) { if (!panningAndScalingEnabled || pointerCaptureInProgress || dragInProgress) {
return false return false
} }
GodotLib.magnify(
detector.focusX, if (detector.scaleFactor >= 0.8f && detector.scaleFactor != 1f && detector.scaleFactor <= 1.2f) {
detector.focusY, GodotLib.magnify(
detector.scaleFactor detector.focusX,
) detector.focusY,
detector.scaleFactor
)
}
return true return true
} }
override fun onScaleBegin(detector: ScaleGestureDetector): Boolean { override fun onScaleBegin(detector: ScaleGestureDetector): Boolean {
if (!panningAndScalingEnabled || pointerCaptureInProgress) { if (!panningAndScalingEnabled || pointerCaptureInProgress || dragInProgress) {
return false return false
} }
scaleInProgress = true scaleInProgress = true

@ -66,6 +66,11 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
private final ScaleGestureDetector scaleGestureDetector; private final ScaleGestureDetector scaleGestureDetector;
private final GodotGestureHandler godotGestureHandler; private final GodotGestureHandler godotGestureHandler;
/**
* Used to decide whether mouse capture can be enabled.
*/
private int lastSeenToolType = MotionEvent.TOOL_TYPE_UNKNOWN;
public GodotInputHandler(GodotRenderView godotView) { public GodotInputHandler(GodotRenderView godotView) {
final Context context = godotView.getView().getContext(); final Context context = godotView.getView().getContext();
mRenderView = godotView; mRenderView = godotView;
@ -105,6 +110,10 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
return (source & InputDevice.SOURCE_JOYSTICK) == InputDevice.SOURCE_JOYSTICK || (source & InputDevice.SOURCE_DPAD) == InputDevice.SOURCE_DPAD || (source & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD; return (source & InputDevice.SOURCE_JOYSTICK) == InputDevice.SOURCE_JOYSTICK || (source & InputDevice.SOURCE_DPAD) == InputDevice.SOURCE_DPAD || (source & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD;
} }
public boolean canCapturePointer() {
return lastSeenToolType == MotionEvent.TOOL_TYPE_MOUSE;
}
public void onPointerCaptureChange(boolean hasCapture) { public void onPointerCaptureChange(boolean hasCapture) {
godotGestureHandler.onPointerCaptureChange(hasCapture); godotGestureHandler.onPointerCaptureChange(hasCapture);
} }
@ -174,6 +183,8 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
} }
public boolean onTouchEvent(final MotionEvent event) { public boolean onTouchEvent(final MotionEvent event) {
lastSeenToolType = event.getToolType(0);
this.scaleGestureDetector.onTouchEvent(event); this.scaleGestureDetector.onTouchEvent(event);
if (this.gestureDetector.onTouchEvent(event)) { if (this.gestureDetector.onTouchEvent(event)) {
// The gesture detector has handled the event. // The gesture detector has handled the event.
@ -198,6 +209,8 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
} }
public boolean onGenericMotionEvent(MotionEvent event) { public boolean onGenericMotionEvent(MotionEvent event) {
lastSeenToolType = event.getToolType(0);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && gestureDetector.onGenericMotionEvent(event)) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && gestureDetector.onGenericMotionEvent(event)) {
// The gesture detector has handled the event. // The gesture detector has handled the event.
return true; return true;
@ -471,15 +484,27 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
} }
static boolean handleMouseEvent(int eventAction, int buttonsMask, float x, float y, float deltaX, float deltaY, boolean doubleClick, boolean sourceMouseRelative) { static boolean handleMouseEvent(int eventAction, int buttonsMask, float x, float y, float deltaX, float deltaY, boolean doubleClick, boolean sourceMouseRelative) {
// Fix the buttonsMask
switch (eventAction) {
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
// Zero-up the button state
buttonsMask = 0;
break;
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_MOVE:
if (buttonsMask == 0) {
buttonsMask = MotionEvent.BUTTON_PRIMARY;
}
break;
}
// We don't handle ACTION_BUTTON_PRESS and ACTION_BUTTON_RELEASE events as they typically // We don't handle ACTION_BUTTON_PRESS and ACTION_BUTTON_RELEASE events as they typically
// follow ACTION_DOWN and ACTION_UP events. As such, handling them would result in duplicate // follow ACTION_DOWN and ACTION_UP events. As such, handling them would result in duplicate
// stream of events to the engine. // stream of events to the engine.
switch (eventAction) { switch (eventAction) {
case MotionEvent.ACTION_CANCEL: case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP: case MotionEvent.ACTION_UP:
// Zero-up the button state
buttonsMask = 0;
// FALL THROUGH
case MotionEvent.ACTION_DOWN: case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_HOVER_ENTER: case MotionEvent.ACTION_HOVER_ENTER:
case MotionEvent.ACTION_HOVER_EXIT: case MotionEvent.ACTION_HOVER_EXIT:

@ -49,6 +49,8 @@ GodotJavaViewWrapper::GodotJavaViewWrapper(jobject godot_view) {
_request_pointer_capture = env->GetMethodID(_cls, "requestPointerCapture", "()V"); _request_pointer_capture = env->GetMethodID(_cls, "requestPointerCapture", "()V");
_release_pointer_capture = env->GetMethodID(_cls, "releasePointerCapture", "()V"); _release_pointer_capture = env->GetMethodID(_cls, "releasePointerCapture", "()V");
} }
_can_capture_pointer = env->GetMethodID(_cls, "canCapturePointer", "()Z");
} }
bool GodotJavaViewWrapper::can_update_pointer_icon() const { bool GodotJavaViewWrapper::can_update_pointer_icon() const {
@ -56,7 +58,16 @@ bool GodotJavaViewWrapper::can_update_pointer_icon() const {
} }
bool GodotJavaViewWrapper::can_capture_pointer() const { bool GodotJavaViewWrapper::can_capture_pointer() const {
return _request_pointer_capture != nullptr && _release_pointer_capture != nullptr; // We can capture the pointer if the other jni capture method ids are initialized,
// and GodotView#canCapturePointer() returns true.
if (_request_pointer_capture != nullptr && _release_pointer_capture != nullptr && _can_capture_pointer != nullptr) {
JNIEnv *env = get_jni_env();
ERR_FAIL_NULL_V(env, false);
return env->CallBooleanMethod(_godot_view, _can_capture_pointer);
}
return false;
} }
void GodotJavaViewWrapper::request_pointer_capture() { void GodotJavaViewWrapper::request_pointer_capture() {

@ -44,6 +44,7 @@ private:
jobject _godot_view; jobject _godot_view;
jmethodID _can_capture_pointer = 0;
jmethodID _request_pointer_capture = 0; jmethodID _request_pointer_capture = 0;
jmethodID _release_pointer_capture = 0; jmethodID _release_pointer_capture = 0;

@ -182,7 +182,7 @@ String OS_Android::get_name() const {
} }
String OS_Android::get_system_property(const char *key) const { String OS_Android::get_system_property(const char *key) const {
static String value; String value;
char value_str[PROP_VALUE_MAX]; char value_str[PROP_VALUE_MAX];
if (__system_property_get(key, value_str)) { if (__system_property_get(key, value_str)) {
value = String(value_str); value = String(value_str);
@ -230,20 +230,20 @@ String OS_Android::get_version() const {
"ro.potato.version", "ro.xtended.version", "org.evolution.version", "ro.corvus.version", "ro.pa.version", "ro.potato.version", "ro.xtended.version", "org.evolution.version", "ro.corvus.version", "ro.pa.version",
"ro.crdroid.version", "ro.syberia.version", "ro.arrow.version", "ro.lineage.version" }; "ro.crdroid.version", "ro.syberia.version", "ro.arrow.version", "ro.lineage.version" };
for (int i = 0; i < roms.size(); i++) { for (int i = 0; i < roms.size(); i++) {
static String rom_version = get_system_property(roms[i]); String rom_version = get_system_property(roms[i]);
if (!rom_version.is_empty()) { if (!rom_version.is_empty()) {
return rom_version; return rom_version;
} }
} }
static String mod_version = get_system_property("ro.modversion"); // Handles other Android custom ROMs. String mod_version = get_system_property("ro.modversion"); // Handles other Android custom ROMs.
if (!mod_version.is_empty()) { if (!mod_version.is_empty()) {
return mod_version; return mod_version;
} }
// Handles stock Android. // Handles stock Android.
static String sdk_version = get_system_property("ro.build.version.sdk_int"); String sdk_version = get_system_property("ro.build.version.sdk_int");
static String build = get_system_property("ro.build.version.incremental"); String build = get_system_property("ro.build.version.incremental");
if (!sdk_version.is_empty()) { if (!sdk_version.is_empty()) {
if (!build.is_empty()) { if (!build.is_empty()) {
return vformat("%s.%s", sdk_version, build); return vformat("%s.%s", sdk_version, build);

@ -74,6 +74,7 @@ void JoypadLinux::Joypad::reset() {
events.clear(); events.clear();
} }
#ifdef UDEV_ENABLED
// This function is derived from SDL: // This function is derived from SDL:
// https://github.com/libsdl-org/SDL/blob/main/src/core/linux/SDL_sandbox.c#L28-L45 // https://github.com/libsdl-org/SDL/blob/main/src/core/linux/SDL_sandbox.c#L28-L45
static bool detect_sandbox() { static bool detect_sandbox() {
@ -93,6 +94,7 @@ static bool detect_sandbox() {
return false; return false;
} }
#endif // UDEV_ENABLED
JoypadLinux::JoypadLinux(Input *in) { JoypadLinux::JoypadLinux(Input *in) {
#ifdef UDEV_ENABLED #ifdef UDEV_ENABLED

@ -210,48 +210,54 @@ String OS_LinuxBSD::get_name() const {
} }
String OS_LinuxBSD::get_systemd_os_release_info_value(const String &key) const { String OS_LinuxBSD::get_systemd_os_release_info_value(const String &key) const {
static String info; Ref<FileAccess> f = FileAccess::open("/etc/os-release", FileAccess::READ);
if (info.is_empty()) { if (f.is_valid()) {
Ref<FileAccess> f = FileAccess::open("/etc/os-release", FileAccess::READ); while (!f->eof_reached()) {
if (f.is_valid()) { const String line = f->get_line();
while (!f->eof_reached()) { if (line.find(key) != -1) {
const String line = f->get_line(); String value = line.split("=")[1].strip_edges();
if (line.find(key) != -1) { value = value.trim_prefix("\"");
return line.split("=")[1].strip_edges(); return value.trim_suffix("\"");
}
} }
} }
} }
return info; return "";
} }
String OS_LinuxBSD::get_distribution_name() const { String OS_LinuxBSD::get_distribution_name() const {
static String systemd_name = get_systemd_os_release_info_value("NAME"); // returns a value for systemd users, otherwise an empty string. static String distribution_name = get_systemd_os_release_info_value("NAME"); // returns a value for systemd users, otherwise an empty string.
if (!systemd_name.is_empty()) { if (!distribution_name.is_empty()) {
return systemd_name; return distribution_name;
} }
struct utsname uts; // returns a decent value for BSD family. struct utsname uts; // returns a decent value for BSD family.
uname(&uts); uname(&uts);
return uts.sysname; distribution_name = uts.sysname;
return distribution_name;
} }
String OS_LinuxBSD::get_version() const { String OS_LinuxBSD::get_version() const {
static String systemd_version = get_systemd_os_release_info_value("VERSION"); // returns a value for systemd users, otherwise an empty string. static String release_version = get_systemd_os_release_info_value("VERSION"); // returns a value for systemd users, otherwise an empty string.
if (!systemd_version.is_empty()) { if (!release_version.is_empty()) {
return systemd_version; return release_version;
} }
struct utsname uts; // returns a decent value for BSD family. struct utsname uts; // returns a decent value for BSD family.
uname(&uts); uname(&uts);
return uts.version; release_version = uts.version;
return release_version;
} }
Vector<String> OS_LinuxBSD::get_video_adapter_driver_info() const { Vector<String> OS_LinuxBSD::get_video_adapter_driver_info() const {
if (RenderingServer::get_singleton()->get_rendering_device() == nullptr) { if (RenderingServer::get_singleton() == nullptr) {
return Vector<String>(); return Vector<String>();
} }
const String rendering_device_name = RenderingServer::get_singleton()->get_rendering_device()->get_device_name(); // e.g. `NVIDIA GeForce GTX 970` static Vector<String> info;
const String rendering_device_vendor = RenderingServer::get_singleton()->get_rendering_device()->get_device_vendor_name(); // e.g. `NVIDIA` if (!info.is_empty()) {
return info;
}
const String rendering_device_name = RenderingServer::get_singleton()->get_video_adapter_name(); // e.g. `NVIDIA GeForce GTX 970`
const String rendering_device_vendor = RenderingServer::get_singleton()->get_video_adapter_vendor(); // e.g. `NVIDIA`
const String card_name = rendering_device_name.trim_prefix(rendering_device_vendor).strip_edges(); // -> `GeForce GTX 970` const String card_name = rendering_device_name.trim_prefix(rendering_device_vendor).strip_edges(); // -> `GeForce GTX 970`
String vendor_device_id_mappings; String vendor_device_id_mappings;
@ -315,8 +321,8 @@ Vector<String> OS_LinuxBSD::get_video_adapter_driver_info() const {
Vector<String> class_display_device_drivers = OS_LinuxBSD::lspci_get_device_value(class_display_device_candidates, kernel_lit, dummys); Vector<String> class_display_device_drivers = OS_LinuxBSD::lspci_get_device_value(class_display_device_candidates, kernel_lit, dummys);
Vector<String> class_3d_device_drivers = OS_LinuxBSD::lspci_get_device_value(class_3d_device_candidates, kernel_lit, dummys); Vector<String> class_3d_device_drivers = OS_LinuxBSD::lspci_get_device_value(class_3d_device_candidates, kernel_lit, dummys);
static String driver_name; String driver_name;
static String driver_version; String driver_version;
// Use first valid value: // Use first valid value:
for (const String &driver : class_3d_device_drivers) { for (const String &driver : class_3d_device_drivers) {
@ -336,7 +342,6 @@ Vector<String> OS_LinuxBSD::get_video_adapter_driver_info() const {
} }
} }
Vector<String> info;
info.push_back(driver_name); info.push_back(driver_name);
String modinfo; String modinfo;

@ -450,8 +450,7 @@ String OS_UWP::get_distribution_name() const {
String OS_UWP::get_version() const { String OS_UWP::get_version() const {
winrt::hstring df_version = VersionInfo().DeviceFamilyVersion(); winrt::hstring df_version = VersionInfo().DeviceFamilyVersion();
static String version = String(winrt::to_string(df_version).c_str()); return String(winrt::to_string(df_version).c_str());
return version;
} }
OS::DateTime OS_UWP::get_datetime(bool p_utc) const { OS::DateTime OS_UWP::get_datetime(bool p_utc) const {

@ -1070,6 +1070,10 @@ bool DisplayServerWeb::can_any_window_draw() const {
return true; return true;
} }
DisplayServer::VSyncMode DisplayServerWeb::window_get_vsync_mode(WindowID p_vsync_mode) const {
return DisplayServer::VSYNC_ENABLED;
}
void DisplayServerWeb::process_events() { void DisplayServerWeb::process_events() {
Input::get_singleton()->flush_buffered_events(); Input::get_singleton()->flush_buffered_events();
if (godot_js_input_gamepad_sample() == OK) { if (godot_js_input_gamepad_sample() == OK) {

@ -213,6 +213,8 @@ public:
virtual bool can_any_window_draw() const override; virtual bool can_any_window_draw() const override;
virtual DisplayServer::VSyncMode window_get_vsync_mode(WindowID p_vsync_mode) const override;
// events // events
virtual void process_events() override; virtual void process_events() override;

@ -352,20 +352,25 @@ String OS_Windows::get_version() const {
} }
Vector<String> OS_Windows::get_video_adapter_driver_info() const { Vector<String> OS_Windows::get_video_adapter_driver_info() const {
if (RenderingServer::get_singleton()->get_rendering_device() == nullptr) { if (RenderingServer::get_singleton() == nullptr) {
return Vector<String>(); return Vector<String>();
} }
static Vector<String> info;
if (!info.is_empty()) {
return info;
}
REFCLSID clsid = CLSID_WbemLocator; // Unmarshaler CLSID REFCLSID clsid = CLSID_WbemLocator; // Unmarshaler CLSID
REFIID uuid = IID_IWbemLocator; // Interface UUID REFIID uuid = IID_IWbemLocator; // Interface UUID
IWbemLocator *wbemLocator = NULL; // to get the services IWbemLocator *wbemLocator = NULL; // to get the services
IWbemServices *wbemServices = NULL; // to get the class IWbemServices *wbemServices = NULL; // to get the class
IEnumWbemClassObject *iter = NULL; IEnumWbemClassObject *iter = NULL;
IWbemClassObject *pnpSDriverObject[1]; // contains driver name, version, etc. IWbemClassObject *pnpSDriverObject[1]; // contains driver name, version, etc.
static String driver_name; String driver_name;
static String driver_version; String driver_version;
const String device_name = RenderingServer::get_singleton()->get_rendering_device()->get_device_name(); const String device_name = RenderingServer::get_singleton()->get_video_adapter_name();
if (device_name.is_empty()) { if (device_name.is_empty()) {
return Vector<String>(); return Vector<String>();
} }
@ -439,7 +444,6 @@ Vector<String> OS_Windows::get_video_adapter_driver_info() const {
SAFE_RELEASE(wbemServices) SAFE_RELEASE(wbemServices)
SAFE_RELEASE(iter) SAFE_RELEASE(iter)
Vector<String> info;
info.push_back(driver_name); info.push_back(driver_name);
info.push_back(driver_version); info.push_back(driver_version);

@ -157,7 +157,6 @@ void AudioStreamPlayer2D::_update_panning() {
Vector2 global_pos = get_global_position(); Vector2 global_pos = get_global_position();
HashSet<Viewport *> viewports = world_2d->get_viewports(); HashSet<Viewport *> viewports = world_2d->get_viewports();
viewports.insert(get_viewport()); // TODO: This is a mediocre workaround for #50958. Remove when that bug is fixed!
volume_vector.resize(4); volume_vector.resize(4);
volume_vector.write[0] = AudioFrame(0, 0); volume_vector.write[0] = AudioFrame(0, 0);
@ -188,11 +187,11 @@ void AudioStreamPlayer2D::_update_panning() {
float dist = global_pos.distance_to(listener_in_global); // Distance to listener, or screen if none. float dist = global_pos.distance_to(listener_in_global); // Distance to listener, or screen if none.
if (dist > max_distance) { if (dist > max_distance) {
continue; //can't hear this sound in this viewport continue; // Can't hear this sound in this viewport.
} }
float multiplier = Math::pow(1.0f - dist / max_distance, attenuation); float multiplier = Math::pow(1.0f - dist / max_distance, attenuation);
multiplier *= Math::db_to_linear(volume_db); //also apply player volume! multiplier *= Math::db_to_linear(volume_db); // Also apply player volume!
float pan = relative_to_listener.x / screen_size.x; float pan = relative_to_listener.x / screen_size.x;
// Don't let the panning effect extend (too far) beyond the screen. // Don't let the panning effect extend (too far) beyond the screen.
@ -206,7 +205,9 @@ void AudioStreamPlayer2D::_update_panning() {
float l = 1.0 - pan; float l = 1.0 - pan;
float r = pan; float r = pan;
volume_vector.write[0] = AudioFrame(l, r) * multiplier; const AudioFrame &prev_sample = volume_vector[0];
AudioFrame new_sample = AudioFrame(l, r) * multiplier;
volume_vector.write[0] = AudioFrame(MAX(prev_sample[0], new_sample[0]), MAX(prev_sample[1], new_sample[1]));
} }
for (const Ref<AudioStreamPlayback> &playback : stream_playbacks) { for (const Ref<AudioStreamPlayback> &playback : stream_playbacks) {

@ -378,15 +378,12 @@ uint64_t Skeleton3D::get_version() const {
} }
void Skeleton3D::add_bone(const String &p_name) { void Skeleton3D::add_bone(const String &p_name) {
ERR_FAIL_COND(p_name.is_empty() || p_name.contains(":") || p_name.contains("/")); ERR_FAIL_COND(p_name.is_empty() || p_name.contains(":") || p_name.contains("/") || name_to_bone_index.has(p_name));
for (int i = 0; i < bones.size(); i++) {
ERR_FAIL_COND(bones[i].name == p_name);
}
Bone b; Bone b;
b.name = p_name; b.name = p_name;
bones.push_back(b); bones.push_back(b);
name_to_bone_index.insert(p_name, bones.size() - 1);
process_order_dirty = true; process_order_dirty = true;
version++; version++;
rest_dirty = true; rest_dirty = true;
@ -395,13 +392,8 @@ void Skeleton3D::add_bone(const String &p_name) {
} }
int Skeleton3D::find_bone(const String &p_name) const { int Skeleton3D::find_bone(const String &p_name) const {
for (int i = 0; i < bones.size(); i++) { const int *bone_index_ptr = name_to_bone_index.getptr(p_name);
if (bones[i].name == p_name) { return bone_index_ptr != nullptr ? *bone_index_ptr : -1;
return i;
}
}
return -1;
} }
String Skeleton3D::get_bone_name(int p_bone) const { String Skeleton3D::get_bone_name(int p_bone) const {
@ -409,17 +401,21 @@ String Skeleton3D::get_bone_name(int p_bone) const {
ERR_FAIL_INDEX_V(p_bone, bone_size, ""); ERR_FAIL_INDEX_V(p_bone, bone_size, "");
return bones[p_bone].name; return bones[p_bone].name;
} }
void Skeleton3D::set_bone_name(int p_bone, const String &p_name) { void Skeleton3D::set_bone_name(int p_bone, const String &p_name) {
const int bone_size = bones.size(); const int bone_size = bones.size();
ERR_FAIL_INDEX(p_bone, bone_size); ERR_FAIL_INDEX(p_bone, bone_size);
for (int i = 0; i < bone_size; i++) { const int *bone_index_ptr = name_to_bone_index.getptr(p_name);
if (i != p_bone) { if (bone_index_ptr != nullptr) {
ERR_FAIL_COND_MSG(bones[i].name == p_name, "Skeleton3D: '" + get_name() + "', bone name: '" + p_name + "' is already exist."); ERR_FAIL_COND_MSG(*bone_index_ptr != p_bone, "Skeleton3D: '" + get_name() + "', bone name: '" + p_name + "' already exists.");
} return; // No need to rename, the bone already has the given name.
} }
name_to_bone_index.erase(bones[p_bone].name);
bones.write[p_bone].name = p_name; bones.write[p_bone].name = p_name;
name_to_bone_index.insert(p_name, p_bone);
version++; version++;
} }
@ -547,6 +543,7 @@ bool Skeleton3D::is_show_rest_only() const {
void Skeleton3D::clear_bones() { void Skeleton3D::clear_bones() {
bones.clear(); bones.clear();
name_to_bone_index.clear();
process_order_dirty = true; process_order_dirty = true;
version++; version++;
_make_dirty(); _make_dirty();

@ -125,6 +125,7 @@ private:
bool process_order_dirty = false; bool process_order_dirty = false;
Vector<int> parentless_bones; Vector<int> parentless_bones;
HashMap<String, int> name_to_bone_index;
void _make_dirty(); void _make_dirty();
bool dirty = false; bool dirty = false;

@ -1766,7 +1766,7 @@ void AnimationPlayer::set_current_animation(const String &p_anim) {
} else if (!is_playing()) { } else if (!is_playing()) {
play(p_anim); play(p_anim);
} else if (playback.assigned != p_anim) { } else if (playback.assigned != p_anim) {
float speed = get_playing_speed(); float speed = playback.current.speed_scale;
play(p_anim, -1.0, speed, signbit(speed)); play(p_anim, -1.0, speed, signbit(speed));
} else { } else {
// Same animation, do not replay from start // Same animation, do not replay from start
@ -1779,7 +1779,7 @@ String AnimationPlayer::get_current_animation() const {
void AnimationPlayer::set_assigned_animation(const String &p_anim) { void AnimationPlayer::set_assigned_animation(const String &p_anim) {
if (is_playing()) { if (is_playing()) {
float speed = get_playing_speed(); float speed = playback.current.speed_scale;
play(p_anim, -1.0, speed, signbit(speed)); play(p_anim, -1.0, speed, signbit(speed));
} else { } else {
ERR_FAIL_COND_MSG(!animation_set.has(p_anim), vformat("Animation not found: %s.", p_anim)); ERR_FAIL_COND_MSG(!animation_set.has(p_anim), vformat("Animation not found: %s.", p_anim));

@ -561,13 +561,14 @@ void ColorPicker::_update_presets() {
#ifdef TOOLS_ENABLED #ifdef TOOLS_ENABLED
if (editor_settings) { if (editor_settings) {
// Only load preset buttons when the only child is the add-preset button. // Rebuild swatch color buttons, keeping the add-preset button in the first position.
if (preset_container->get_child_count() == 1) { for (int i = 1; i < preset_container->get_child_count(); i++) {
for (int i = 0; i < preset_cache.size(); i++) { preset_container->get_child(i)->queue_free();
_add_preset_button(preset_size, preset_cache[i]);
}
_notification(NOTIFICATION_VISIBILITY_CHANGED);
} }
for (int i = 0; i < preset_cache.size(); i++) {
_add_preset_button(preset_size, preset_cache[i]);
}
_notification(NOTIFICATION_VISIBILITY_CHANGED);
} }
#endif #endif
} }

@ -39,7 +39,7 @@
void AcceptDialog::_input_from_window(const Ref<InputEvent> &p_event) { void AcceptDialog::_input_from_window(const Ref<InputEvent> &p_event) {
Ref<InputEventKey> key = p_event; Ref<InputEventKey> key = p_event;
if (close_on_escape && key.is_valid() && key->is_pressed() && key->get_keycode() == Key::ESCAPE) { if (close_on_escape && key.is_valid() && key->is_action_pressed(SNAME("ui_cancel"), false, true)) {
_cancel_pressed(); _cancel_pressed();
} }
} }

@ -1366,12 +1366,13 @@ void ItemList::_notification(int p_what) {
text_ofs += base_ofs; text_ofs += base_ofs;
text_ofs += items[i].rect_cache.position; text_ofs += items[i].rect_cache.position;
float text_w = width - text_ofs.x;
items.write[i].text_buf->set_width(text_w);
if (rtl) { if (rtl) {
text_ofs.x = size.width - text_ofs.x - max_len; text_ofs.x = size.width - width;
} }
items.write[i].text_buf->set_width(width - text_ofs.x);
if (rtl) { if (rtl) {
items.write[i].text_buf->set_alignment(HORIZONTAL_ALIGNMENT_RIGHT); items.write[i].text_buf->set_alignment(HORIZONTAL_ALIGNMENT_RIGHT);
} else { } else {

@ -36,7 +36,7 @@
void Popup::_input_from_window(const Ref<InputEvent> &p_event) { void Popup::_input_from_window(const Ref<InputEvent> &p_event) {
Ref<InputEventKey> key = p_event; Ref<InputEventKey> key = p_event;
if (get_flag(FLAG_POPUP) && key.is_valid() && key->is_pressed() && key->get_keycode() == Key::ESCAPE) { if (get_flag(FLAG_POPUP) && key.is_valid() && key->is_action_pressed(SNAME("ui_cancel"), false, true)) {
_close_pressed(); _close_pressed();
} }
} }

@ -177,7 +177,7 @@ void ViewportTexture::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_viewport_path_in_scene", "path"), &ViewportTexture::set_viewport_path_in_scene); ClassDB::bind_method(D_METHOD("set_viewport_path_in_scene", "path"), &ViewportTexture::set_viewport_path_in_scene);
ClassDB::bind_method(D_METHOD("get_viewport_path_in_scene"), &ViewportTexture::get_viewport_path_in_scene); ClassDB::bind_method(D_METHOD("get_viewport_path_in_scene"), &ViewportTexture::get_viewport_path_in_scene);
ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "viewport_path", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "SubViewport", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_NODE_PATH_FROM_SCENE_ROOT), "set_viewport_path_in_scene", "get_viewport_path_in_scene"); ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "viewport_path", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Viewport", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_NODE_PATH_FROM_SCENE_ROOT), "set_viewport_path_in_scene", "get_viewport_path_in_scene");
} }
ViewportTexture::ViewportTexture() { ViewportTexture::ViewportTexture() {
@ -544,6 +544,7 @@ void Viewport::_notification(int p_what) {
gui.mouse_in_viewport = false; gui.mouse_in_viewport = false;
_drop_physics_mouseover(); _drop_physics_mouseover();
_drop_mouse_over(); _drop_mouse_over();
_gui_cancel_tooltip();
// When the mouse exits the viewport, we want to end mouse_over, but // When the mouse exits the viewport, we want to end mouse_over, but
// not mouse_focus, because, for example, we want to continue // not mouse_focus, because, for example, we want to continue
// dragging a scrollbar even if the mouse has left the viewport. // dragging a scrollbar even if the mouse has left the viewport.
@ -1080,6 +1081,10 @@ void Viewport::set_world_2d(const Ref<World2D> &p_world_2d) {
RenderingServer::get_singleton()->viewport_remove_canvas(viewport, current_canvas); RenderingServer::get_singleton()->viewport_remove_canvas(viewport, current_canvas);
} }
if (world_2d.is_valid()) {
world_2d->remove_viewport(this);
}
if (p_world_2d.is_valid()) { if (p_world_2d.is_valid()) {
world_2d = p_world_2d; world_2d = p_world_2d;
} else { } else {
@ -1087,6 +1092,7 @@ void Viewport::set_world_2d(const Ref<World2D> &p_world_2d) {
world_2d = Ref<World2D>(memnew(World2D)); world_2d = Ref<World2D>(memnew(World2D));
} }
world_2d->register_viewport(this);
_update_audio_listener_2d(); _update_audio_listener_2d();
if (is_inside_tree()) { if (is_inside_tree()) {
@ -2828,6 +2834,7 @@ bool Viewport::_sub_windows_forward_input(const Ref<InputEvent> &p_event) {
void Viewport::push_input(const Ref<InputEvent> &p_event, bool p_local_coords) { void Viewport::push_input(const Ref<InputEvent> &p_event, bool p_local_coords) {
ERR_FAIL_COND(!is_inside_tree()); ERR_FAIL_COND(!is_inside_tree());
ERR_FAIL_COND(p_event.is_null());
if (disable_input) { if (disable_input) {
return; return;
@ -4155,6 +4162,7 @@ void Viewport::_validate_property(PropertyInfo &p_property) const {
Viewport::Viewport() { Viewport::Viewport() {
world_2d = Ref<World2D>(memnew(World2D)); world_2d = Ref<World2D>(memnew(World2D));
world_2d->register_viewport(this);
viewport = RenderingServer::get_singleton()->viewport_create(); viewport = RenderingServer::get_singleton()->viewport_create();
texture_rid = RenderingServer::get_singleton()->viewport_get_texture(viewport); texture_rid = RenderingServer::get_singleton()->viewport_get_texture(viewport);

@ -695,6 +695,8 @@ void register_scene_types() {
GDREGISTER_CLASS(VisualShaderNodeParticleAccelerator); GDREGISTER_CLASS(VisualShaderNodeParticleAccelerator);
GDREGISTER_CLASS(VisualShaderNodeParticleEmit); GDREGISTER_CLASS(VisualShaderNodeParticleEmit);
GDREGISTER_VIRTUAL_CLASS(Material);
GDREGISTER_CLASS(PlaceholderMaterial);
GDREGISTER_CLASS(ShaderMaterial); GDREGISTER_CLASS(ShaderMaterial);
GDREGISTER_ABSTRACT_CLASS(CanvasItem); GDREGISTER_ABSTRACT_CLASS(CanvasItem);
GDREGISTER_CLASS(CanvasTexture); GDREGISTER_CLASS(CanvasTexture);
@ -802,11 +804,9 @@ void register_scene_types() {
GDREGISTER_CLASS(TubeTrailMesh); GDREGISTER_CLASS(TubeTrailMesh);
GDREGISTER_CLASS(RibbonTrailMesh); GDREGISTER_CLASS(RibbonTrailMesh);
GDREGISTER_CLASS(PointMesh); GDREGISTER_CLASS(PointMesh);
GDREGISTER_VIRTUAL_CLASS(Material);
GDREGISTER_ABSTRACT_CLASS(BaseMaterial3D); GDREGISTER_ABSTRACT_CLASS(BaseMaterial3D);
GDREGISTER_CLASS(StandardMaterial3D); GDREGISTER_CLASS(StandardMaterial3D);
GDREGISTER_CLASS(ORMMaterial3D); GDREGISTER_CLASS(ORMMaterial3D);
GDREGISTER_CLASS(PlaceholderMaterial);
GDREGISTER_CLASS(ProceduralSkyMaterial); GDREGISTER_CLASS(ProceduralSkyMaterial);
GDREGISTER_CLASS(PanoramaSkyMaterial); GDREGISTER_CLASS(PanoramaSkyMaterial);
GDREGISTER_CLASS(PhysicalSkyMaterial); GDREGISTER_CLASS(PhysicalSkyMaterial);

@ -82,6 +82,14 @@ PhysicsDirectSpaceState2D *World2D::get_direct_space_state() {
return PhysicsServer2D::get_singleton()->space_get_direct_state(get_space()); return PhysicsServer2D::get_singleton()->space_get_direct_state(get_space());
} }
void World2D::register_viewport(Viewport *p_viewport) {
viewports.insert(p_viewport);
}
void World2D::remove_viewport(Viewport *p_viewport) {
viewports.erase(p_viewport);
}
World2D::World2D() { World2D::World2D() {
canvas = RenderingServer::get_singleton()->canvas_create(); canvas = RenderingServer::get_singleton()->canvas_create();
} }

@ -52,9 +52,6 @@ protected:
static void _bind_methods(); static void _bind_methods();
friend class Viewport; friend class Viewport;
void _register_viewport(Viewport *p_viewport);
void _remove_viewport(Viewport *p_viewport);
public: public:
RID get_canvas() const; RID get_canvas() const;
RID get_space() const; RID get_space() const;
@ -62,6 +59,9 @@ public:
PhysicsDirectSpaceState2D *get_direct_space_state(); PhysicsDirectSpaceState2D *get_direct_space_state();
void register_viewport(Viewport *p_viewport);
void remove_viewport(Viewport *p_viewport);
_FORCE_INLINE_ const HashSet<Viewport *> &get_viewports() { return viewports; } _FORCE_INLINE_ const HashSet<Viewport *> &get_viewports() { return viewports; }
World2D(); World2D();

@ -76,7 +76,7 @@ void RendererCanvasCull::_render_canvas_item_tree(RID p_to_render_target, Canvas
} }
} }
void _collect_ysort_children(RendererCanvasCull::Item *p_canvas_item, Transform2D p_transform, RendererCanvasCull::Item *p_material_owner, RendererCanvasCull::Item **r_items, int &r_index, int p_z) { void _collect_ysort_children(RendererCanvasCull::Item *p_canvas_item, Transform2D p_transform, RendererCanvasCull::Item *p_material_owner, const Color &p_modulate, RendererCanvasCull::Item **r_items, int &r_index, int p_z) {
int child_item_count = p_canvas_item->child_items.size(); int child_item_count = p_canvas_item->child_items.size();
RendererCanvasCull::Item **child_items = p_canvas_item->child_items.ptrw(); RendererCanvasCull::Item **child_items = p_canvas_item->child_items.ptrw();
for (int i = 0; i < child_item_count; i++) { for (int i = 0; i < child_item_count; i++) {
@ -87,6 +87,7 @@ void _collect_ysort_children(RendererCanvasCull::Item *p_canvas_item, Transform2
child_items[i]->ysort_xform = p_transform; child_items[i]->ysort_xform = p_transform;
child_items[i]->ysort_pos = p_transform.xform(child_items[i]->xform.columns[2]); child_items[i]->ysort_pos = p_transform.xform(child_items[i]->xform.columns[2]);
child_items[i]->material_owner = child_items[i]->use_parent_material ? p_material_owner : nullptr; child_items[i]->material_owner = child_items[i]->use_parent_material ? p_material_owner : nullptr;
child_items[i]->ysort_modulate = p_modulate;
child_items[i]->ysort_index = r_index; child_items[i]->ysort_index = r_index;
child_items[i]->ysort_parent_abs_z_index = p_z; child_items[i]->ysort_parent_abs_z_index = p_z;
@ -101,7 +102,7 @@ void _collect_ysort_children(RendererCanvasCull::Item *p_canvas_item, Transform2
r_index++; r_index++;
if (child_items[i]->sort_y) { if (child_items[i]->sort_y) {
_collect_ysort_children(child_items[i], p_transform * child_items[i]->xform, child_items[i]->use_parent_material ? p_material_owner : child_items[i], r_items, r_index, abs_z); _collect_ysort_children(child_items[i], p_transform * child_items[i]->xform, child_items[i]->use_parent_material ? p_material_owner : child_items[i], p_modulate * child_items[i]->modulate, r_items, r_index, abs_z);
} }
} }
} }
@ -301,7 +302,7 @@ void RendererCanvasCull::_cull_canvas_item(Item *p_canvas_item, const Transform2
if (allow_y_sort) { if (allow_y_sort) {
if (ci->ysort_children_count == -1) { if (ci->ysort_children_count == -1) {
ci->ysort_children_count = 0; ci->ysort_children_count = 0;
_collect_ysort_children(ci, Transform2D(), p_material_owner, nullptr, ci->ysort_children_count, p_z); _collect_ysort_children(ci, Transform2D(), p_material_owner, Color(1, 1, 1, 1), nullptr, ci->ysort_children_count, p_z);
} }
child_item_count = ci->ysort_children_count + 1; child_item_count = ci->ysort_children_count + 1;
@ -310,14 +311,15 @@ void RendererCanvasCull::_cull_canvas_item(Item *p_canvas_item, const Transform2
ci->ysort_parent_abs_z_index = parent_z; ci->ysort_parent_abs_z_index = parent_z;
child_items[0] = ci; child_items[0] = ci;
int i = 1; int i = 1;
_collect_ysort_children(ci, Transform2D(), p_material_owner, child_items, i, p_z); _collect_ysort_children(ci, Transform2D(), p_material_owner, Color(1, 1, 1, 1), child_items, i, p_z);
ci->ysort_xform = ci->xform.affine_inverse(); ci->ysort_xform = ci->xform.affine_inverse();
ci->ysort_modulate = Color(1, 1, 1, 1);
SortArray<Item *, ItemPtrSort> sorter; SortArray<Item *, ItemPtrSort> sorter;
sorter.sort(child_items, child_item_count); sorter.sort(child_items, child_item_count);
for (i = 0; i < child_item_count; i++) { for (i = 0; i < child_item_count; i++) {
_cull_canvas_item(child_items[i], xform * child_items[i]->ysort_xform, p_clip_rect, modulate, child_items[i]->ysort_parent_abs_z_index, r_z_list, r_z_last_list, (Item *)ci->final_clip_owner, (Item *)child_items[i]->material_owner, false, canvas_cull_mask); _cull_canvas_item(child_items[i], xform * child_items[i]->ysort_xform, p_clip_rect, modulate * child_items[i]->ysort_modulate, child_items[i]->ysort_parent_abs_z_index, r_z_list, r_z_last_list, (Item *)ci->final_clip_owner, (Item *)child_items[i]->material_owner, false, canvas_cull_mask);
} }
} else { } else {
RendererCanvasRender::Item *canvas_group_from = nullptr; RendererCanvasRender::Item *canvas_group_from = nullptr;

@ -312,11 +312,21 @@ ALBEDO = vec3(1.0);
void Fog::free_fog_shader() { void Fog::free_fog_shader() {
MaterialStorage *material_storage = MaterialStorage::get_singleton(); MaterialStorage *material_storage = MaterialStorage::get_singleton();
volumetric_fog.process_shader.version_free(volumetric_fog.process_shader_version); if (volumetric_fog.process_shader_version.is_valid()) {
RD::get_singleton()->free(volumetric_fog.volume_ubo); volumetric_fog.process_shader.version_free(volumetric_fog.process_shader_version);
RD::get_singleton()->free(volumetric_fog.params_ubo); }
material_storage->shader_free(volumetric_fog.default_shader); if (volumetric_fog.volume_ubo.is_valid()) {
material_storage->material_free(volumetric_fog.default_material); RD::get_singleton()->free(volumetric_fog.volume_ubo);
}
if (volumetric_fog.params_ubo.is_valid()) {
RD::get_singleton()->free(volumetric_fog.params_ubo);
}
if (volumetric_fog.default_shader.is_valid()) {
material_storage->shader_free(volumetric_fog.default_shader);
}
if (volumetric_fog.default_material.is_valid()) {
material_storage->material_free(volumetric_fog.default_material);
}
} }
void Fog::FogShaderData::set_code(const String &p_code) { void Fog::FogShaderData::set_code(const String &p_code) {

@ -3581,18 +3581,40 @@ void GI::init(SkyRD *p_sky) {
} }
void GI::free() { void GI::free() {
RD::get_singleton()->free(default_voxel_gi_buffer); if (default_voxel_gi_buffer.is_valid()) {
RD::get_singleton()->free(voxel_gi_lights_uniform); RD::get_singleton()->free(default_voxel_gi_buffer);
RD::get_singleton()->free(sdfgi_ubo); }
if (voxel_gi_lights_uniform.is_valid()) {
voxel_gi_debug_shader.version_free(voxel_gi_debug_shader_version); RD::get_singleton()->free(voxel_gi_lights_uniform);
voxel_gi_shader.version_free(voxel_gi_lighting_shader_version); }
shader.version_free(shader_version); if (sdfgi_ubo.is_valid()) {
sdfgi_shader.debug_probes.version_free(sdfgi_shader.debug_probes_shader); RD::get_singleton()->free(sdfgi_ubo);
sdfgi_shader.debug.version_free(sdfgi_shader.debug_shader); }
sdfgi_shader.direct_light.version_free(sdfgi_shader.direct_light_shader);
sdfgi_shader.integrate.version_free(sdfgi_shader.integrate_shader); if (voxel_gi_debug_shader_version.is_valid()) {
sdfgi_shader.preprocess.version_free(sdfgi_shader.preprocess_shader); voxel_gi_debug_shader.version_free(voxel_gi_debug_shader_version);
}
if (voxel_gi_lighting_shader_version.is_valid()) {
voxel_gi_shader.version_free(voxel_gi_lighting_shader_version);
}
if (shader_version.is_valid()) {
shader.version_free(shader_version);
}
if (sdfgi_shader.debug_probes_shader.is_valid()) {
sdfgi_shader.debug_probes.version_free(sdfgi_shader.debug_probes_shader);
}
if (sdfgi_shader.debug_shader.is_valid()) {
sdfgi_shader.debug.version_free(sdfgi_shader.debug_shader);
}
if (sdfgi_shader.direct_light_shader.is_valid()) {
sdfgi_shader.direct_light.version_free(sdfgi_shader.direct_light_shader);
}
if (sdfgi_shader.integrate_shader.is_valid()) {
sdfgi_shader.integrate.version_free(sdfgi_shader.integrate_shader);
}
if (sdfgi_shader.preprocess_shader.is_valid()) {
sdfgi_shader.preprocess.version_free(sdfgi_shader.preprocess_shader);
}
if (voxel_gi_lights) { if (voxel_gi_lights) {
memdelete_arr(voxel_gi_lights); memdelete_arr(voxel_gi_lights);

@ -964,7 +964,12 @@ void RendererSceneRenderRD::render_scene(const Ref<RenderSceneBuffers> &p_render
scene_data.z_far = p_camera_data->main_projection.get_z_far(); scene_data.z_far = p_camera_data->main_projection.get_z_far();
// this should be the same for all cameras.. // this should be the same for all cameras..
scene_data.lod_distance_multiplier = p_camera_data->main_projection.get_lod_multiplier(); const float lod_distance_multiplier = p_camera_data->main_projection.get_lod_multiplier();
// Also, take into account resolution scaling for the multiplier, since we have more leeway with quality
// degradation visibility. Conversely, allow upwards scaling, too, for increased mesh detail at high res.
const float scaling_3d_scale = GLOBAL_GET("rendering/scaling_3d/scale");
scene_data.lod_distance_multiplier = lod_distance_multiplier * (1.0 / scaling_3d_scale);
if (get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_DISABLE_LOD) { if (get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_DISABLE_LOD) {
scene_data.screen_mesh_lod_threshold = 0.0; scene_data.screen_mesh_lod_threshold = 0.0;

@ -627,7 +627,7 @@ AABB MeshStorage::mesh_get_aabb(RID p_mesh, RID p_skeleton) {
if (skeleton->use_2d) { if (skeleton->use_2d) {
for (int j = 0; j < bs; j++) { for (int j = 0; j < bs; j++) {
if (skbones[0].size == Vector3()) { if (skbones[j].size == Vector3(-1, -1, -1)) {
continue; //bone is unused continue; //bone is unused
} }
@ -654,7 +654,7 @@ AABB MeshStorage::mesh_get_aabb(RID p_mesh, RID p_skeleton) {
} }
} else { } else {
for (int j = 0; j < bs; j++) { for (int j = 0; j < bs; j++) {
if (skbones[0].size == Vector3()) { if (skbones[j].size == Vector3(-1, -1, -1)) {
continue; //bone is unused continue; //bone is unused
} }

@ -1153,8 +1153,8 @@ void TextureStorage::texture_3d_update(RID p_texture, const Vector<Ref<Image>> &
image = image->duplicate(); image = image->duplicate();
image->convert(tex->validated_format); image->convert(tex->validated_format);
} }
all_data_size += images[i]->get_data().size(); all_data_size += image->get_data().size();
images.push_back(image); images.write[i] = image;
} }
all_data.resize(all_data_size); //consolidate all data here all_data.resize(all_data_size); //consolidate all data here

@ -34,7 +34,6 @@
#include "core/object/worker_thread_pool.h" #include "core/object/worker_thread_pool.h"
#include "core/os/os.h" #include "core/os/os.h"
#include "rendering_server_default.h" #include "rendering_server_default.h"
#include "rendering_server_globals.h"
#include <new> #include <new>

@ -42,6 +42,7 @@
#include "servers/rendering/renderer_scene_occlusion_cull.h" #include "servers/rendering/renderer_scene_occlusion_cull.h"
#include "servers/rendering/renderer_scene_render.h" #include "servers/rendering/renderer_scene_render.h"
#include "servers/rendering/rendering_method.h" #include "servers/rendering/rendering_method.h"
#include "servers/rendering/rendering_server_globals.h"
#include "servers/rendering/storage/utilities.h" #include "servers/rendering/storage/utilities.h"
#include "servers/xr/xr_interface.h" #include "servers/xr/xr_interface.h"
@ -516,6 +517,29 @@ public:
} else if (p_dependency == instance->skeleton) { } else if (p_dependency == instance->skeleton) {
singleton->instance_attach_skeleton(instance->self, RID()); singleton->instance_attach_skeleton(instance->self, RID());
} else { } else {
// It's possible the same material is used in multiple slots,
// so we check whether we need to clear them all.
if (p_dependency == instance->material_override) {
singleton->instance_geometry_set_material_override(instance->self, RID());
}
if (p_dependency == instance->material_overlay) {
singleton->instance_geometry_set_material_overlay(instance->self, RID());
}
for (int i = 0; i < instance->materials.size(); i++) {
if (p_dependency == instance->materials[i]) {
singleton->instance_set_surface_override_material(instance->self, i, RID());
}
}
if (instance->base_type == RS::INSTANCE_PARTICLES) {
RID particle_material = RSG::particles_storage->particles_get_process_material(instance->base);
if (p_dependency == particle_material) {
RSG::particles_storage->particles_set_process_material(instance->base, RID());
}
}
// Even if no change is made we still need to call `_instance_queue_update`.
// This dependency could also be a result of the freed material being used
// by the mesh this mesh instance uses.
singleton->_instance_queue_update(instance, false, true); singleton->_instance_queue_update(instance, false, true);
} }
} }

@ -693,6 +693,7 @@ PackedInt32Array TextServer::shaped_text_get_line_breaks_adv(const RID &p_shaped
real_t width = 0.f; real_t width = 0.f;
int line_start = MAX(p_start, range.x); int line_start = MAX(p_start, range.x);
int last_end = line_start;
int prev_safe_break = 0; int prev_safe_break = 0;
int last_safe_break = -1; int last_safe_break = -1;
int word_count = 0; int word_count = 0;
@ -718,12 +719,18 @@ PackedInt32Array TextServer::shaped_text_get_line_breaks_adv(const RID &p_shaped
while ((start_pos < end_pos) && ((l_gl[end_pos].flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE || (l_gl[end_pos].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD || (l_gl[end_pos].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT)) { while ((start_pos < end_pos) && ((l_gl[end_pos].flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE || (l_gl[end_pos].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD || (l_gl[end_pos].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT)) {
end_pos -= l_gl[end_pos].count; end_pos -= l_gl[end_pos].count;
} }
lines.push_back(l_gl[start_pos].start); if (last_end <= l_gl[start_pos].start) {
lines.push_back(l_gl[end_pos].end); lines.push_back(l_gl[start_pos].start);
lines.push_back(l_gl[end_pos].end);
last_end = l_gl[end_pos].end;
}
trim_next = true; trim_next = true;
} else { } else {
lines.push_back(line_start); if (last_end <= line_start) {
lines.push_back(l_gl[last_safe_break].end); lines.push_back(line_start);
lines.push_back(l_gl[last_safe_break].end);
last_end = l_gl[last_safe_break].end;
}
} }
line_start = l_gl[last_safe_break].end; line_start = l_gl[last_safe_break].end;
prev_safe_break = last_safe_break + 1; prev_safe_break = last_safe_break + 1;
@ -751,12 +758,18 @@ PackedInt32Array TextServer::shaped_text_get_line_breaks_adv(const RID &p_shaped
while ((start_pos < end_pos) && ((l_gl[end_pos].flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE || (l_gl[end_pos].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD || (l_gl[end_pos].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT)) { while ((start_pos < end_pos) && ((l_gl[end_pos].flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE || (l_gl[end_pos].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD || (l_gl[end_pos].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT)) {
end_pos -= l_gl[end_pos].count; end_pos -= l_gl[end_pos].count;
} }
lines.push_back(l_gl[start_pos].start); if (last_end <= l_gl[start_pos].start) {
lines.push_back(l_gl[end_pos].end); lines.push_back(l_gl[start_pos].start);
lines.push_back(l_gl[end_pos].end);
last_end = l_gl[end_pos].end;
}
trim_next = false; trim_next = false;
} else { } else {
lines.push_back(line_start); if (last_end <= line_start) {
lines.push_back(l_gl[i].end); lines.push_back(line_start);
lines.push_back(l_gl[i].end);
last_end = l_gl[i].end;
}
} }
line_start = l_gl[i].end; line_start = l_gl[i].end;
prev_safe_break = i + 1; prev_safe_break = i + 1;
@ -812,6 +825,7 @@ PackedInt32Array TextServer::shaped_text_get_line_breaks(const RID &p_shaped, do
double width = 0.f; double width = 0.f;
int line_start = MAX(p_start, range.x); int line_start = MAX(p_start, range.x);
int last_end = line_start;
int prev_safe_break = 0; int prev_safe_break = 0;
int last_safe_break = -1; int last_safe_break = -1;
int word_count = 0; int word_count = 0;
@ -836,12 +850,18 @@ PackedInt32Array TextServer::shaped_text_get_line_breaks(const RID &p_shaped, do
while ((start_pos < end_pos) && ((l_gl[end_pos].flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE || (l_gl[end_pos].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD || (l_gl[end_pos].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT)) { while ((start_pos < end_pos) && ((l_gl[end_pos].flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE || (l_gl[end_pos].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD || (l_gl[end_pos].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT)) {
end_pos -= l_gl[end_pos].count; end_pos -= l_gl[end_pos].count;
} }
lines.push_back(l_gl[start_pos].start); if (last_end <= l_gl[start_pos].start) {
lines.push_back(l_gl[end_pos].end); lines.push_back(l_gl[start_pos].start);
lines.push_back(l_gl[end_pos].end);
last_end = l_gl[end_pos].end;
}
trim_next = true; trim_next = true;
} else { } else {
lines.push_back(line_start); if (last_end <= line_start) {
lines.push_back(l_gl[last_safe_break].end); lines.push_back(line_start);
lines.push_back(l_gl[last_safe_break].end);
last_end = l_gl[last_safe_break].end;
}
} }
line_start = l_gl[last_safe_break].end; line_start = l_gl[last_safe_break].end;
prev_safe_break = last_safe_break + 1; prev_safe_break = last_safe_break + 1;
@ -863,11 +883,17 @@ PackedInt32Array TextServer::shaped_text_get_line_breaks(const RID &p_shaped, do
end_pos -= l_gl[end_pos].count; end_pos -= l_gl[end_pos].count;
} }
trim_next = false; trim_next = false;
lines.push_back(l_gl[start_pos].start); if (last_end <= l_gl[start_pos].start) {
lines.push_back(l_gl[end_pos].end); lines.push_back(l_gl[start_pos].start);
lines.push_back(l_gl[end_pos].end);
last_end = l_gl[end_pos].end;
}
} else { } else {
lines.push_back(line_start); if (last_end <= line_start) {
lines.push_back(l_gl[i].end); lines.push_back(line_start);
lines.push_back(l_gl[i].end);
last_end = l_gl[i].end;
}
} }
line_start = l_gl[i].end; line_start = l_gl[i].end;
prev_safe_break = i + 1; prev_safe_break = i + 1;

@ -45,6 +45,7 @@
#include <cmath> #include <cmath>
#endif #endif
#include <cstdio> #include <cstdio>
#include <cstdint>
#include <cstdlib> #include <cstdlib>
#include <list> #include <list>
#include <map> #include <map>

@ -0,0 +1,12 @@
diff --git a/thirdparty/openxr/src/common/platform_utils.hpp b/thirdparty/openxr/src/common/platform_utils.hpp
index 85d5cdab10..2d870cfea7 100644
--- a/thirdparty/openxr/src/common/platform_utils.hpp
+++ b/thirdparty/openxr/src/common/platform_utils.hpp
@@ -11,6 +11,7 @@
#include "xr_dependencies.h"
#include <string>
+#include <stdint.h>
#include <stdlib.h>
// OpenXR paths and registry key locations

@ -11,6 +11,7 @@
#include "xr_dependencies.h" #include "xr_dependencies.h"
#include <string> #include <string>
#include <stdint.h>
#include <stdlib.h> #include <stdlib.h>
// OpenXR paths and registry key locations // OpenXR paths and registry key locations

@ -13,3 +13,26 @@ index 132bdcfb3e..925584cf52 100644
namespace VHACD { namespace VHACD {
//! Incremental Convex Hull algorithm (cf. http://cs.smith.edu/~orourke/books/ftp.html ). //! Incremental Convex Hull algorithm (cf. http://cs.smith.edu/~orourke/books/ftp.html ).
enum ICHullError { enum ICHullError {
diff --git a/thirdparty/vhacd/inc/vhacdManifoldMesh.h b/thirdparty/vhacd/inc/vhacdManifoldMesh.h
index a48f53c5c5..5eed4e13aa 100644
--- a/thirdparty/vhacd/inc/vhacdManifoldMesh.h
+++ b/thirdparty/vhacd/inc/vhacdManifoldMesh.h
@@ -18,6 +18,11 @@ All rights reserved.
#include "vhacdCircularList.h"
#include "vhacdSArray.h"
#include "vhacdVector.h"
+
+// -- GODOT start --
+#include <cstdint>
+// -- GODOT end --
+
namespace VHACD {
class TMMTriangle;
class TMMEdge;
@@ -139,4 +144,4 @@ private:
friend class ICHull;
};
}
-#endif // VHACD_MANIFOLD_MESH_H
\ No newline at end of file
+#endif // VHACD_MANIFOLD_MESH_H

@ -18,6 +18,11 @@ All rights reserved.
#include "vhacdCircularList.h" #include "vhacdCircularList.h"
#include "vhacdSArray.h" #include "vhacdSArray.h"
#include "vhacdVector.h" #include "vhacdVector.h"
// -- GODOT start --
#include <cstdint>
// -- GODOT end --
namespace VHACD { namespace VHACD {
class TMMTriangle; class TMMTriangle;
class TMMEdge; class TMMEdge;
@ -139,4 +144,4 @@ private:
friend class ICHull; friend class ICHull;
}; };
} }
#endif // VHACD_MANIFOLD_MESH_H #endif // VHACD_MANIFOLD_MESH_H