summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--doc/classes/CanvasItem.xml2
-rw-r--r--doc/classes/RenderingServer.xml1
-rw-r--r--drivers/gles3/rasterizer_scene_gles3.cpp2
-rw-r--r--editor/animation_bezier_editor.cpp4
-rw-r--r--editor/editor_file_system.cpp46
-rw-r--r--editor/editor_file_system.h3
-rw-r--r--editor/editor_node.cpp9
-rw-r--r--editor/editor_node.h3
-rw-r--r--editor/gui/editor_file_dialog.cpp3
-rw-r--r--editor/gui/scene_tree_editor.cpp49
-rw-r--r--editor/gui/scene_tree_editor.h9
-rw-r--r--editor/plugins/visual_shader_editor_plugin.cpp2
-rw-r--r--editor/scene_tree_dock.cpp1
-rw-r--r--platform/windows/display_server_windows.cpp106
-rw-r--r--platform/windows/display_server_windows.h10
-rw-r--r--scene/gui/file_dialog.cpp3
-rw-r--r--scene/gui/line_edit.cpp52
-rw-r--r--scene/gui/text_edit.cpp19
-rw-r--r--servers/rendering/renderer_rd/environment/sky.cpp2
-rw-r--r--tests/scene/test_audio_stream_wav.h18
-rw-r--r--tests/scene/test_text_edit.h44
-rw-r--r--tests/test_main.cpp2
22 files changed, 283 insertions, 107 deletions
diff --git a/doc/classes/CanvasItem.xml b/doc/classes/CanvasItem.xml
index 186ee1b9c4..0a0223c550 100644
--- a/doc/classes/CanvasItem.xml
+++ b/doc/classes/CanvasItem.xml
@@ -96,6 +96,7 @@
<param index="3" name="texture" type="Texture2D" default="null" />
<description>
Draws a colored polygon of any number of points, convex or concave. Unlike [method draw_polygon], a single color must be specified for the whole polygon.
+ [b]Note:[/b] If you frequently redraw the same polygon with a large number of vertices, consider pre-calculating the triangulation with [method Geometry2D.triangulate_polygon] and using [method draw_mesh], [method draw_multimesh], or [method RenderingServer.canvas_item_add_triangle_array].
</description>
</method>
<method name="draw_dashed_line">
@@ -251,6 +252,7 @@
<param index="3" name="texture" type="Texture2D" default="null" />
<description>
Draws a solid polygon of any number of points, convex or concave. Unlike [method draw_colored_polygon], each point's color can be changed individually. See also [method draw_polyline] and [method draw_polyline_colors]. If you need more flexibility (such as being able to use bones), use [method RenderingServer.canvas_item_add_triangle_array] instead.
+ [b]Note:[/b] If you frequently redraw the same polygon with a large number of vertices, consider pre-calculating the triangulation with [method Geometry2D.triangulate_polygon] and using [method draw_mesh], [method draw_multimesh], or [method RenderingServer.canvas_item_add_triangle_array].
</description>
</method>
<method name="draw_polyline">
diff --git a/doc/classes/RenderingServer.xml b/doc/classes/RenderingServer.xml
index 4cdfba17e9..cea9a4cec4 100644
--- a/doc/classes/RenderingServer.xml
+++ b/doc/classes/RenderingServer.xml
@@ -329,6 +329,7 @@
<param index="4" name="texture" type="RID" default="RID()" />
<description>
Draws a 2D polygon on the [CanvasItem] pointed to by the [param item] [RID]. If you need more flexibility (such as being able to use bones), use [method canvas_item_add_triangle_array] instead. See also [method CanvasItem.draw_polygon].
+ [b]Note:[/b] If you frequently redraw the same polygon with a large number of vertices, consider pre-calculating the triangulation with [method Geometry2D.triangulate_polygon] and using [method CanvasItem.draw_mesh], [method CanvasItem.draw_multimesh], or [method canvas_item_add_triangle_array].
</description>
</method>
<method name="canvas_item_add_polyline">
diff --git a/drivers/gles3/rasterizer_scene_gles3.cpp b/drivers/gles3/rasterizer_scene_gles3.cpp
index 3ed8042f3f..8b6d3e3268 100644
--- a/drivers/gles3/rasterizer_scene_gles3.cpp
+++ b/drivers/gles3/rasterizer_scene_gles3.cpp
@@ -912,7 +912,7 @@ void RasterizerSceneGLES3::_update_sky_radiance(RID p_env, const Projection &p_p
RS::SkyMode sky_mode = sky->mode;
if (sky_mode == RS::SKY_MODE_AUTOMATIC) {
- if (shader_data->uses_time || shader_data->uses_position) {
+ if ((shader_data->uses_time || shader_data->uses_position) && sky->radiance_size == 256) {
update_single_frame = true;
sky_mode = RS::SKY_MODE_REALTIME;
} else if (shader_data->uses_light || shader_data->ubo_size > 0) {
diff --git a/editor/animation_bezier_editor.cpp b/editor/animation_bezier_editor.cpp
index feb5f3d20d..24fcfd3930 100644
--- a/editor/animation_bezier_editor.cpp
+++ b/editor/animation_bezier_editor.cpp
@@ -174,7 +174,7 @@ void AnimationBezierTrackEdit::_draw_track(int p_track, const Color &p_color) {
}
if (lines.size() >= 2) {
- draw_multiline(lines, p_color, Math::round(EDSCALE));
+ draw_multiline(lines, p_color, Math::round(EDSCALE), true);
}
}
}
@@ -208,7 +208,7 @@ void AnimationBezierTrackEdit::_draw_line_clipped(const Vector2 &p_from, const V
from = from.lerp(to, c);
}
- draw_line(from, to, p_color, Math::round(EDSCALE));
+ draw_line(from, to, p_color, Math::round(EDSCALE), true);
}
void AnimationBezierTrackEdit::_notification(int p_what) {
diff --git a/editor/editor_file_system.cpp b/editor/editor_file_system.cpp
index 3d3caf59eb..e13a984213 100644
--- a/editor/editor_file_system.cpp
+++ b/editor/editor_file_system.cpp
@@ -410,11 +410,13 @@ void EditorFileSystem::_scan_filesystem() {
new_filesystem->parent = nullptr;
ScannedDirectory *sd;
+ HashSet<String> *processed_files = nullptr;
// On the first scan, the first_scan_root_dir is created in _first_scan_filesystem.
if (first_scan) {
sd = first_scan_root_dir;
// Will be updated on scan.
ResourceUID::get_singleton()->clear();
+ processed_files = memnew(HashSet<String>());
} else {
Ref<DirAccess> d = DirAccess::create(DirAccess::ACCESS_RESOURCES);
sd = memnew(ScannedDirectory);
@@ -422,14 +424,18 @@ void EditorFileSystem::_scan_filesystem() {
nb_files_total = _scan_new_dir(sd, d);
}
- _process_file_system(sd, new_filesystem, sp);
+ _process_file_system(sd, new_filesystem, sp, processed_files);
+ if (first_scan) {
+ _process_removed_files(*processed_files);
+ }
dep_update_list.clear();
file_cache.clear(); //clear caches, no longer needed
if (first_scan) {
memdelete(first_scan_root_dir);
first_scan_root_dir = nullptr;
+ memdelete(processed_files);
} else {
//on the first scan this is done from the main thread after re-importing
_save_filesystem_cache();
@@ -990,7 +996,7 @@ int EditorFileSystem::_scan_new_dir(ScannedDirectory *p_dir, Ref<DirAccess> &da)
return nb_files_total_scan;
}
-void EditorFileSystem::_process_file_system(const ScannedDirectory *p_scan_dir, EditorFileSystemDirectory *p_dir, ScanProgress &p_progress) {
+void EditorFileSystem::_process_file_system(const ScannedDirectory *p_scan_dir, EditorFileSystemDirectory *p_dir, ScanProgress &p_progress, HashSet<String> *r_processed_files) {
p_dir->modified_time = FileAccess::get_modified_time(p_scan_dir->full_path);
for (ScannedDirectory *scan_sub_dir : p_scan_dir->subdirs) {
@@ -998,7 +1004,7 @@ void EditorFileSystem::_process_file_system(const ScannedDirectory *p_scan_dir,
sub_dir->parent = p_dir;
sub_dir->name = scan_sub_dir->name;
p_dir->subdirs.push_back(sub_dir);
- _process_file_system(scan_sub_dir, sub_dir, p_progress);
+ _process_file_system(scan_sub_dir, sub_dir, p_progress, r_processed_files);
}
for (const String &scan_file : p_scan_dir->files) {
@@ -1014,6 +1020,10 @@ void EditorFileSystem::_process_file_system(const ScannedDirectory *p_scan_dir,
fi->file = scan_file;
p_dir->files.push_back(fi);
+ if (r_processed_files) {
+ r_processed_files->insert(path);
+ }
+
FileCache *fc = file_cache.getptr(path);
uint64_t mt = FileAccess::get_modified_time(path);
@@ -1139,6 +1149,20 @@ void EditorFileSystem::_process_file_system(const ScannedDirectory *p_scan_dir,
}
}
+void EditorFileSystem::_process_removed_files(const HashSet<String> &p_processed_files) {
+ for (const KeyValue<String, EditorFileSystem::FileCache> &kv : file_cache) {
+ if (!p_processed_files.has(kv.key)) {
+ if (ClassDB::is_parent_class(kv.value.type, SNAME("Script"))) {
+ // A script has been removed from disk since the last startup. The documentation needs to be updated.
+ // There's no need to add the path in update_script_paths since that is exclusively for updating global class names,
+ // which is handled in _first_scan_filesystem before the full scan to ensure plugins and autoloads can be created.
+ MutexLock update_script_lock(update_script_mutex);
+ update_script_paths_documentation.insert(kv.key);
+ }
+ }
+ }
+}
+
void EditorFileSystem::_scan_fs_changes(EditorFileSystemDirectory *p_dir, ScanProgress &p_progress) {
uint64_t current_mtime = FileAccess::get_modified_time(p_dir->get_path());
@@ -1206,7 +1230,7 @@ void EditorFileSystem::_scan_fs_changes(EditorFileSystemDirectory *p_dir, ScanPr
int nb_files_dir = _scan_new_dir(&sd, d);
p_progress.hi += nb_files_dir;
diff_nb_files += nb_files_dir;
- _process_file_system(&sd, efd, p_progress);
+ _process_file_system(&sd, efd, p_progress, nullptr);
ItemAction ia;
ia.action = ItemAction::ACTION_DIR_ADD;
@@ -1872,7 +1896,12 @@ void EditorFileSystem::_update_script_classes() {
EditorProgress *ep = nullptr;
if (update_script_paths.size() > 1) {
- ep = memnew(EditorProgress("update_scripts_classes", TTR("Registering global classes..."), update_script_paths.size()));
+ if (MessageQueue::get_singleton()->is_flushing()) {
+ // Use background progress when message queue is flushing.
+ ep = memnew(EditorProgress("update_scripts_classes", TTR("Registering global classes..."), update_script_paths.size(), false, true));
+ } else {
+ ep = memnew(EditorProgress("update_scripts_classes", TTR("Registering global classes..."), update_script_paths.size()));
+ }
}
int step_count = 0;
@@ -1911,7 +1940,12 @@ void EditorFileSystem::_update_script_documentation() {
EditorProgress *ep = nullptr;
if (update_script_paths_documentation.size() > 1) {
- ep = memnew(EditorProgress("update_script_paths_documentation", TTR("Updating scripts documentation"), update_script_paths_documentation.size()));
+ if (MessageQueue::get_singleton()->is_flushing()) {
+ // Use background progress when message queue is flushing.
+ ep = memnew(EditorProgress("update_script_paths_documentation", TTR("Updating scripts documentation"), update_script_paths_documentation.size(), false, true));
+ } else {
+ ep = memnew(EditorProgress("update_script_paths_documentation", TTR("Updating scripts documentation"), update_script_paths_documentation.size()));
+ }
}
int step_count = 0;
diff --git a/editor/editor_file_system.h b/editor/editor_file_system.h
index 2ceb3fe4a5..7848ede8a7 100644
--- a/editor/editor_file_system.h
+++ b/editor/editor_file_system.h
@@ -239,7 +239,7 @@ class EditorFileSystem : public Node {
HashSet<String> import_extensions;
int _scan_new_dir(ScannedDirectory *p_dir, Ref<DirAccess> &da);
- void _process_file_system(const ScannedDirectory *p_scan_dir, EditorFileSystemDirectory *p_dir, ScanProgress &p_progress);
+ void _process_file_system(const ScannedDirectory *p_scan_dir, EditorFileSystemDirectory *p_dir, ScanProgress &p_progress, HashSet<String> *p_processed_files);
Thread thread_sources;
bool scanning_changes = false;
@@ -287,6 +287,7 @@ class EditorFileSystem : public Node {
void _update_script_classes();
void _update_script_documentation();
void _process_update_pending();
+ void _process_removed_files(const HashSet<String> &p_processed_files);
Mutex update_scene_mutex;
HashSet<String> update_scene_paths;
diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp
index 47b5fa4d1a..f154cbd1e2 100644
--- a/editor/editor_node.cpp
+++ b/editor/editor_node.cpp
@@ -175,7 +175,7 @@ static const String REMOVE_ANDROID_BUILD_TEMPLATE_MESSAGE = "The Android build t
static const String INSTALL_ANDROID_BUILD_TEMPLATE_MESSAGE = "This will set up your project for gradle Android builds by installing the source template to \"%s\".\nNote that in order to make gradle builds instead of using pre-built APKs, the \"Use Gradle Build\" option should be enabled in the Android export preset.";
bool EditorProgress::step(const String &p_state, int p_step, bool p_force_refresh) {
- if (Thread::is_main_thread()) {
+ if (!force_background && Thread::is_main_thread()) {
return EditorNode::progress_task_step(task, p_state, p_step, p_force_refresh);
} else {
EditorNode::progress_task_step_bg(task, p_step);
@@ -183,17 +183,18 @@ bool EditorProgress::step(const String &p_state, int p_step, bool p_force_refres
}
}
-EditorProgress::EditorProgress(const String &p_task, const String &p_label, int p_amount, bool p_can_cancel) {
- if (Thread::is_main_thread()) {
+EditorProgress::EditorProgress(const String &p_task, const String &p_label, int p_amount, bool p_can_cancel, bool p_force_background) {
+ if (!p_force_background && Thread::is_main_thread()) {
EditorNode::progress_add_task(p_task, p_label, p_amount, p_can_cancel);
} else {
EditorNode::progress_add_task_bg(p_task, p_label, p_amount);
}
task = p_task;
+ force_background = p_force_background;
}
EditorProgress::~EditorProgress() {
- if (Thread::is_main_thread()) {
+ if (!force_background && Thread::is_main_thread()) {
EditorNode::progress_end_task(task);
} else {
EditorNode::progress_end_task_bg(task);
diff --git a/editor/editor_node.h b/editor/editor_node.h
index bdf9b26a7a..e24bae73f0 100644
--- a/editor/editor_node.h
+++ b/editor/editor_node.h
@@ -124,9 +124,10 @@ class WindowWrapper;
struct EditorProgress {
String task;
+ bool force_background = false;
bool step(const String &p_state, int p_step = -1, bool p_force_refresh = true);
- EditorProgress(const String &p_task, const String &p_label, int p_amount, bool p_can_cancel = false);
+ EditorProgress(const String &p_task, const String &p_label, int p_amount, bool p_can_cancel = false, bool p_force_background = false);
~EditorProgress();
};
diff --git a/editor/gui/editor_file_dialog.cpp b/editor/gui/editor_file_dialog.cpp
index 7aa19509e1..b6aa3c2215 100644
--- a/editor/gui/editor_file_dialog.cpp
+++ b/editor/gui/editor_file_dialog.cpp
@@ -115,6 +115,8 @@ void EditorFileDialog::_native_dialog_cb(bool p_ok, const Vector<String> &p_file
file_name = ProjectSettings::get_singleton()->localize_path(file_name);
}
}
+ selected_options = p_selected_options;
+
String f = files[0];
if (mode == FILE_MODE_OPEN_FILES) {
emit_signal(SNAME("files_selected"), files);
@@ -146,7 +148,6 @@ void EditorFileDialog::_native_dialog_cb(bool p_ok, const Vector<String> &p_file
}
file->set_text(f);
dir->set_text(f.get_base_dir());
- selected_options = p_selected_options;
filter->select(p_filter);
}
diff --git a/editor/gui/scene_tree_editor.cpp b/editor/gui/scene_tree_editor.cpp
index 52ba98b4d5..2de22238f4 100644
--- a/editor/gui/scene_tree_editor.cpp
+++ b/editor/gui/scene_tree_editor.cpp
@@ -174,15 +174,40 @@ void SceneTreeEditor::_cell_button_pressed(Object *p_item, int p_column, int p_i
EditorDockManager::get_singleton()->focus_dock(NodeDock::get_singleton());
NodeDock::get_singleton()->show_groups();
} else if (p_id == BUTTON_UNIQUE) {
- undo_redo->create_action(TTR("Disable Scene Unique Name"));
- undo_redo->add_do_method(n, "set_unique_name_in_owner", false);
- undo_redo->add_undo_method(n, "set_unique_name_in_owner", true);
- undo_redo->add_do_method(this, "_update_tree");
- undo_redo->add_undo_method(this, "_update_tree");
- undo_redo->commit_action();
+ bool ask_before_revoking_unique_name = EDITOR_GET("docks/scene_tree/ask_before_revoking_unique_name");
+ revoke_node = n;
+ if (ask_before_revoking_unique_name) {
+ String msg = vformat(TTR("Revoke unique name for node \"%s\"?"), n->get_name());
+ ask_before_revoke_checkbox->set_pressed(false);
+ revoke_dialog_label->set_text(msg);
+ revoke_dialog->reset_size();
+ revoke_dialog->popup_centered();
+ } else {
+ _revoke_unique_name();
+ }
}
}
+void SceneTreeEditor::_update_ask_before_revoking_unique_name() {
+ if (ask_before_revoke_checkbox->is_pressed()) {
+ EditorSettings::get_singleton()->set("docks/scene_tree/ask_before_revoking_unique_name", false);
+ ask_before_revoke_checkbox->set_pressed(false);
+ }
+
+ _revoke_unique_name();
+}
+
+void SceneTreeEditor::_revoke_unique_name() {
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
+
+ undo_redo->create_action(TTR("Disable Scene Unique Name"));
+ undo_redo->add_do_method(revoke_node, "set_unique_name_in_owner", false);
+ undo_redo->add_undo_method(revoke_node, "set_unique_name_in_owner", true);
+ undo_redo->add_do_method(this, "_update_tree");
+ undo_redo->add_undo_method(this, "_update_tree");
+ undo_redo->commit_action();
+}
+
void SceneTreeEditor::_toggle_visible(Node *p_node) {
if (p_node->has_method("is_visible") && p_node->has_method("set_visible")) {
bool v = bool(p_node->call("is_visible"));
@@ -1620,6 +1645,18 @@ SceneTreeEditor::SceneTreeEditor(bool p_label, bool p_can_rename, bool p_can_ope
update_node_tooltip_delay->set_one_shot(true);
add_child(update_node_tooltip_delay);
+ revoke_dialog = memnew(ConfirmationDialog);
+ revoke_dialog->set_ok_button_text(TTR("Revoke"));
+ add_child(revoke_dialog);
+ revoke_dialog->connect(SceneStringName(confirmed), callable_mp(this, &SceneTreeEditor::_update_ask_before_revoking_unique_name));
+ VBoxContainer *vb = memnew(VBoxContainer);
+ revoke_dialog->add_child(vb);
+ revoke_dialog_label = memnew(Label);
+ vb->add_child(revoke_dialog_label);
+ ask_before_revoke_checkbox = memnew(CheckBox(TTR("Don't Ask Again")));
+ ask_before_revoke_checkbox->set_tooltip_text(TTR("This dialog can also be enabled/disabled in the Editor Settings: Docks > Scene Tree > Ask Before Revoking Unique Name."));
+ vb->add_child(ask_before_revoke_checkbox);
+
script_types = memnew(List<StringName>);
ClassDB::get_inheriters_from_class("Script", script_types);
}
diff --git a/editor/gui/scene_tree_editor.h b/editor/gui/scene_tree_editor.h
index b4d9644f16..9633993b8b 100644
--- a/editor/gui/scene_tree_editor.h
+++ b/editor/gui/scene_tree_editor.h
@@ -31,6 +31,7 @@
#ifndef SCENE_TREE_EDITOR_H
#define SCENE_TREE_EDITOR_H
+#include "scene/gui/check_box.h"
#include "scene/gui/check_button.h"
#include "scene/gui/dialogs.h"
#include "scene/gui/tree.h"
@@ -68,6 +69,11 @@ class SceneTreeEditor : public Control {
AcceptDialog *error = nullptr;
AcceptDialog *warning = nullptr;
+ ConfirmationDialog *revoke_dialog = nullptr;
+ Label *revoke_dialog_label = nullptr;
+ CheckBox *ask_before_revoke_checkbox = nullptr;
+ Node *revoke_node = nullptr;
+
bool auto_expand_selected = true;
bool connect_to_script_mode = false;
bool connecting_signal = false;
@@ -144,6 +150,9 @@ class SceneTreeEditor : public Control {
Vector<StringName> valid_types;
+ void _update_ask_before_revoking_unique_name();
+ void _revoke_unique_name();
+
public:
// Public for use with callable_mp.
void _update_tree(bool p_scroll_to_selected = false);
diff --git a/editor/plugins/visual_shader_editor_plugin.cpp b/editor/plugins/visual_shader_editor_plugin.cpp
index 054239d99f..bf8dab92f8 100644
--- a/editor/plugins/visual_shader_editor_plugin.cpp
+++ b/editor/plugins/visual_shader_editor_plugin.cpp
@@ -2995,8 +2995,8 @@ void VisualShaderEditor::_frame_title_popup_show(const Point2 &p_position, int p
}
frame_title_change_edit->set_text(node->get_title());
frame_title_change_popup->set_meta("id", p_node_id);
- frame_title_change_popup->popup();
frame_title_change_popup->set_position(p_position);
+ frame_title_change_popup->popup();
// Select current text.
frame_title_change_edit->grab_focus();
diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp
index baa77cb41d..9978c55d7b 100644
--- a/editor/scene_tree_dock.cpp
+++ b/editor/scene_tree_dock.cpp
@@ -4655,6 +4655,7 @@ SceneTreeDock::SceneTreeDock(Node *p_scene_root, EditorSelection *p_editor_selec
EDITOR_DEF("interface/editors/show_scene_tree_root_selection", true);
EDITOR_DEF("interface/editors/derive_script_globals_by_name", true);
EDITOR_DEF("docks/scene_tree/ask_before_deleting_related_animation_tracks", true);
+ EDITOR_DEF("docks/scene_tree/ask_before_revoking_unique_name", true);
EDITOR_DEF("_use_favorites_root_selection", false);
Resource::_update_configuration_warning = _update_configuration_warning;
diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp
index 602ca5f52e..50ebe7077f 100644
--- a/platform/windows/display_server_windows.cpp
+++ b/platform/windows/display_server_windows.cpp
@@ -522,7 +522,7 @@ void DisplayServerWindows::_thread_fd_monitor(void *p_ud) {
if (!item.has("name") || !item.has("values") || !item.has("default")) {
continue;
}
- event_handler->add_option(pfdc, item["name"], item["values"], item["default_idx"]);
+ event_handler->add_option(pfdc, item["name"], item["values"], item["default"]);
}
event_handler->set_root(fd->root);
@@ -625,62 +625,41 @@ void DisplayServerWindows::_thread_fd_monitor(void *p_ud) {
}
}
if (fd->callback.is_valid()) {
- if (fd->options_in_cb) {
- Variant v_result = true;
- Variant v_files = file_names;
- Variant v_index = index;
- Variant v_opt = options;
- const Variant *cb_args[4] = { &v_result, &v_files, &v_index, &v_opt };
-
- fd->callback.call_deferredp(cb_args, 4);
- } else {
- Variant v_result = true;
- Variant v_files = file_names;
- Variant v_index = index;
- const Variant *cb_args[3] = { &v_result, &v_files, &v_index };
-
- fd->callback.call_deferredp(cb_args, 3);
- }
+ MutexLock lock(ds->file_dialog_mutex);
+ FileDialogCallback cb;
+ cb.callback = fd->callback;
+ cb.status = true;
+ cb.files = file_names;
+ cb.index = index;
+ cb.options = options;
+ cb.opt_in_cb = fd->options_in_cb;
+ ds->pending_cbs.push_back(cb);
}
} else {
if (fd->callback.is_valid()) {
- if (fd->options_in_cb) {
- Variant v_result = false;
- Variant v_files = Vector<String>();
- Variant v_index = 0;
- Variant v_opt = Dictionary();
- const Variant *cb_args[4] = { &v_result, &v_files, &v_index, &v_opt };
-
- fd->callback.call_deferredp(cb_args, 4);
- } else {
- Variant v_result = false;
- Variant v_files = Vector<String>();
- Variant v_index = 0;
- const Variant *cb_args[3] = { &v_result, &v_files, &v_index };
-
- fd->callback.call_deferredp(cb_args, 3);
- }
+ MutexLock lock(ds->file_dialog_mutex);
+ FileDialogCallback cb;
+ cb.callback = fd->callback;
+ cb.status = false;
+ cb.files = Vector<String>();
+ cb.index = index;
+ cb.options = options;
+ cb.opt_in_cb = fd->options_in_cb;
+ ds->pending_cbs.push_back(cb);
}
}
pfd->Release();
} else {
if (fd->callback.is_valid()) {
- if (fd->options_in_cb) {
- Variant v_result = false;
- Variant v_files = Vector<String>();
- Variant v_index = 0;
- Variant v_opt = Dictionary();
- const Variant *cb_args[4] = { &v_result, &v_files, &v_index, &v_opt };
-
- fd->callback.call_deferredp(cb_args, 4);
- } else {
- Variant v_result = false;
- Variant v_files = Vector<String>();
- Variant v_index = 0;
- const Variant *cb_args[3] = { &v_result, &v_files, &v_index };
-
- fd->callback.call_deferredp(cb_args, 3);
- }
+ MutexLock lock(ds->file_dialog_mutex);
+ FileDialogCallback cb;
+ cb.callback = fd->callback;
+ cb.status = false;
+ cb.files = Vector<String>();
+ cb.index = 0;
+ cb.options = Dictionary();
+ cb.opt_in_cb = fd->options_in_cb;
+ ds->pending_cbs.push_back(cb);
}
}
{
@@ -768,6 +747,34 @@ Error DisplayServerWindows::_file_dialog_with_options_show(const String &p_title
return OK;
}
+void DisplayServerWindows::process_file_dialog_callbacks() {
+ MutexLock lock(file_dialog_mutex);
+ while (!pending_cbs.is_empty()) {
+ FileDialogCallback cb = pending_cbs.front()->get();
+ pending_cbs.pop_front();
+
+ if (cb.opt_in_cb) {
+ Variant ret;
+ Callable::CallError ce;
+ const Variant *args[4] = { &cb.status, &cb.files, &cb.index, &cb.options };
+
+ cb.callback.callp(args, 4, ret, ce);
+ if (ce.error != Callable::CallError::CALL_OK) {
+ ERR_PRINT(vformat("Failed to execute file dialog callback: %s.", Variant::get_callable_error_text(cb.callback, args, 4, ce)));
+ }
+ } else {
+ Variant ret;
+ Callable::CallError ce;
+ const Variant *args[3] = { &cb.status, &cb.files, &cb.index };
+
+ cb.callback.callp(args, 3, ret, ce);
+ if (ce.error != Callable::CallError::CALL_OK) {
+ ERR_PRINT(vformat("Failed to execute file dialog callback: %s.", Variant::get_callable_error_text(cb.callback, args, 3, ce)));
+ }
+ }
+ }
+}
+
void DisplayServerWindows::mouse_set_mode(MouseMode p_mode) {
_THREAD_SAFE_METHOD_
@@ -3190,6 +3197,7 @@ void DisplayServerWindows::process_events() {
memdelete(E->get());
E->erase();
}
+ process_file_dialog_callbacks();
}
void DisplayServerWindows::force_process_and_drop_events() {
diff --git a/platform/windows/display_server_windows.h b/platform/windows/display_server_windows.h
index 3deb7ac8b0..54e1c9681d 100644
--- a/platform/windows/display_server_windows.h
+++ b/platform/windows/display_server_windows.h
@@ -572,6 +572,16 @@ class DisplayServerWindows : public DisplayServer {
Mutex file_dialog_mutex;
List<FileDialogData *> file_dialogs;
HashMap<HWND, FileDialogData *> file_dialog_wnd;
+ struct FileDialogCallback {
+ Callable callback;
+ Variant status;
+ Variant files;
+ Variant index;
+ Variant options;
+ bool opt_in_cb = false;
+ };
+ List<FileDialogCallback> pending_cbs;
+ void process_file_dialog_callbacks();
static void _thread_fd_monitor(void *p_ud);
diff --git a/scene/gui/file_dialog.cpp b/scene/gui/file_dialog.cpp
index 1a1aa5ccb0..373488b0fc 100644
--- a/scene/gui/file_dialog.cpp
+++ b/scene/gui/file_dialog.cpp
@@ -124,6 +124,8 @@ void FileDialog::_native_dialog_cb(bool p_ok, const Vector<String> &p_files, int
file_name = ProjectSettings::get_singleton()->localize_path(file_name);
}
}
+ selected_options = p_selected_options;
+
String f = files[0];
if (mode == FILE_MODE_OPEN_FILES) {
emit_signal(SNAME("files_selected"), files);
@@ -155,7 +157,6 @@ void FileDialog::_native_dialog_cb(bool p_ok, const Vector<String> &p_files, int
}
file->set_text(f);
dir->set_text(f.get_base_dir());
- selected_options = p_selected_options;
filter->select(p_filter);
}
diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp
index df668aa496..c2818edd9c 100644
--- a/scene/gui/line_edit.cpp
+++ b/scene/gui/line_edit.cpp
@@ -68,10 +68,15 @@ void LineEdit::_move_caret_left(bool p_select, bool p_move_by_word) {
int cc = caret_column;
PackedInt32Array words = TS->shaped_text_get_word_breaks(text_rid);
- for (int i = words.size() - 2; i >= 0; i = i - 2) {
- if (words[i] < cc) {
- cc = words[i];
- break;
+ if (words.is_empty() || cc <= words[0]) {
+ // Move to the start when there are no more words.
+ cc = 0;
+ } else {
+ for (int i = words.size() - 2; i >= 0; i = i - 2) {
+ if (words[i] < cc) {
+ cc = words[i];
+ break;
+ }
}
}
@@ -101,10 +106,15 @@ void LineEdit::_move_caret_right(bool p_select, bool p_move_by_word) {
int cc = caret_column;
PackedInt32Array words = TS->shaped_text_get_word_breaks(text_rid);
- for (int i = 1; i < words.size(); i = i + 2) {
- if (words[i] > cc) {
- cc = words[i];
- break;
+ if (words.is_empty() || cc >= words[words.size() - 1]) {
+ // Move to the end when there are no more words.
+ cc = text.length();
+ } else {
+ for (int i = 1; i < words.size(); i = i + 2) {
+ if (words[i] > cc) {
+ cc = words[i];
+ break;
+ }
}
}
@@ -159,10 +169,15 @@ void LineEdit::_backspace(bool p_word, bool p_all_to_left) {
int cc = caret_column;
PackedInt32Array words = TS->shaped_text_get_word_breaks(text_rid);
- for (int i = words.size() - 2; i >= 0; i = i - 2) {
- if (words[i] < cc) {
- cc = words[i];
- break;
+ if (words.is_empty() || cc <= words[0]) {
+ // Delete to the start when there are no more words.
+ cc = 0;
+ } else {
+ for (int i = words.size() - 2; i >= 0; i = i - 2) {
+ if (words[i] < cc) {
+ cc = words[i];
+ break;
+ }
}
}
@@ -198,10 +213,15 @@ void LineEdit::_delete(bool p_word, bool p_all_to_right) {
if (p_word) {
int cc = caret_column;
PackedInt32Array words = TS->shaped_text_get_word_breaks(text_rid);
- for (int i = 1; i < words.size(); i = i + 2) {
- if (words[i] > cc) {
- cc = words[i];
- break;
+ if (words.is_empty() || cc >= words[words.size() - 1]) {
+ // Delete to the end when there are no more words.
+ cc = text.length();
+ } else {
+ for (int i = 1; i < words.size(); i = i + 2) {
+ if (words[i] > cc) {
+ cc = words[i];
+ break;
+ }
}
}
diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp
index a2f39af858..c4b33fd889 100644
--- a/scene/gui/text_edit.cpp
+++ b/scene/gui/text_edit.cpp
@@ -2393,7 +2393,7 @@ void TextEdit::_move_caret_left(bool p_select, bool p_move_by_word) {
} else {
PackedInt32Array words = TS->shaped_text_get_word_breaks(text.get_line_data(get_caret_line(i))->get_rid());
if (words.is_empty() || cc <= words[0]) {
- // This solves the scenario where there are no words but glyfs that can be ignored.
+ // Move to the start when there are no more words.
cc = 0;
} else {
for (int j = words.size() - 2; j >= 0; j = j - 2) {
@@ -2450,7 +2450,7 @@ void TextEdit::_move_caret_right(bool p_select, bool p_move_by_word) {
} else {
PackedInt32Array words = TS->shaped_text_get_word_breaks(text.get_line_data(get_caret_line(i))->get_rid());
if (words.is_empty() || cc >= words[words.size() - 1]) {
- // This solves the scenario where there are no words but glyfs that can be ignored.
+ // Move to the end when there are no more words.
cc = text[get_caret_line(i)].length();
} else {
for (int j = 1; j < words.size(); j = j + 2) {
@@ -2666,7 +2666,7 @@ void TextEdit::_do_backspace(bool p_word, bool p_all_to_left) {
// Get a list with the indices of the word bounds of the given text line.
const PackedInt32Array words = TS->shaped_text_get_word_breaks(text.get_line_data(get_caret_line(caret_index))->get_rid());
if (words.is_empty() || column <= words[0]) {
- // If "words" is empty, meaning no words are left, we can remove everything until the beginning of the line.
+ // Delete to the start when there are no more words.
column = 0;
} else {
// Otherwise search for the first word break that is smaller than the index from we're currently deleting.
@@ -2731,10 +2731,15 @@ void TextEdit::_delete(bool p_word, bool p_all_to_right) {
int column = get_caret_column(caret_index);
PackedInt32Array words = TS->shaped_text_get_word_breaks(text.get_line_data(line)->get_rid());
- for (int j = 1; j < words.size(); j = j + 2) {
- if (words[j] > column) {
- column = words[j];
- break;
+ if (words.is_empty() || column >= words[words.size() - 1]) {
+ // Delete to the end when there are no more words.
+ column = text[get_caret_line(i)].length();
+ } else {
+ for (int j = 1; j < words.size(); j = j + 2) {
+ if (words[j] > column) {
+ column = words[j];
+ break;
+ }
}
}
diff --git a/servers/rendering/renderer_rd/environment/sky.cpp b/servers/rendering/renderer_rd/environment/sky.cpp
index 7d4ce6888f..2087989102 100644
--- a/servers/rendering/renderer_rd/environment/sky.cpp
+++ b/servers/rendering/renderer_rd/environment/sky.cpp
@@ -1250,7 +1250,7 @@ void SkyRD::update_radiance_buffers(Ref<RenderSceneBuffersRD> p_render_buffers,
RS::SkyMode sky_mode = sky->mode;
if (sky_mode == RS::SKY_MODE_AUTOMATIC) {
- if (shader_data->uses_time || shader_data->uses_position) {
+ if ((shader_data->uses_time || shader_data->uses_position) && sky->radiance_size == 256) {
update_single_frame = true;
sky_mode = RS::SKY_MODE_REALTIME;
} else if (shader_data->uses_light || shader_data->ubo_size > 0) {
diff --git a/tests/scene/test_audio_stream_wav.h b/tests/scene/test_audio_stream_wav.h
index 5166cd3c13..d3d5cc8a30 100644
--- a/tests/scene/test_audio_stream_wav.h
+++ b/tests/scene/test_audio_stream_wav.h
@@ -181,27 +181,27 @@ void run_test(String file_name, AudioStreamWAV::Format data_format, bool stereo,
}
}
-TEST_CASE("[AudioStreamWAV] Mono PCM8 format") {
+TEST_CASE("[Audio][AudioStreamWAV] Mono PCM8 format") {
run_test("test_pcm8_mono.wav", AudioStreamWAV::FORMAT_8_BITS, false, WAV_RATE, WAV_COUNT);
}
-TEST_CASE("[AudioStreamWAV] Mono PCM16 format") {
+TEST_CASE("[Audio][AudioStreamWAV] Mono PCM16 format") {
run_test("test_pcm16_mono.wav", AudioStreamWAV::FORMAT_16_BITS, false, WAV_RATE, WAV_COUNT);
}
-TEST_CASE("[AudioStreamWAV] Stereo PCM8 format") {
+TEST_CASE("[Audio][AudioStreamWAV] Stereo PCM8 format") {
run_test("test_pcm8_stereo.wav", AudioStreamWAV::FORMAT_8_BITS, true, WAV_RATE, WAV_COUNT);
}
-TEST_CASE("[AudioStreamWAV] Stereo PCM16 format") {
+TEST_CASE("[Audio][AudioStreamWAV] Stereo PCM16 format") {
run_test("test_pcm16_stereo.wav", AudioStreamWAV::FORMAT_16_BITS, true, WAV_RATE, WAV_COUNT);
}
-TEST_CASE("[AudioStreamWAV] Alternate mix rate") {
+TEST_CASE("[Audio][AudioStreamWAV] Alternate mix rate") {
run_test("test_pcm16_stereo_38000Hz.wav", AudioStreamWAV::FORMAT_16_BITS, true, 38000, 38000);
}
-TEST_CASE("[AudioStreamWAV] save_to_wav() adds '.wav' file extension automatically") {
+TEST_CASE("[Audio][AudioStreamWAV] save_to_wav() adds '.wav' file extension automatically") {
String save_path = TestUtils::get_temp_path("test_wav_extension");
Vector<uint8_t> test_data = gen_pcm8_test(WAV_RATE, WAV_COUNT, false);
Ref<AudioStreamWAV> stream = memnew(AudioStreamWAV);
@@ -213,7 +213,7 @@ TEST_CASE("[AudioStreamWAV] save_to_wav() adds '.wav' file extension automatical
CHECK(error == OK);
}
-TEST_CASE("[AudioStreamWAV] Default values") {
+TEST_CASE("[Audio][AudioStreamWAV] Default values") {
Ref<AudioStreamWAV> stream = memnew(AudioStreamWAV);
CHECK(stream->get_format() == AudioStreamWAV::FORMAT_8_BITS);
CHECK(stream->get_loop_mode() == AudioStreamWAV::LOOP_DISABLED);
@@ -227,11 +227,11 @@ TEST_CASE("[AudioStreamWAV] Default values") {
CHECK(stream->get_stream_name() == "");
}
-TEST_CASE("[AudioStreamWAV] Save empty file") {
+TEST_CASE("[Audio][AudioStreamWAV] Save empty file") {
run_test("test_empty.wav", AudioStreamWAV::FORMAT_8_BITS, false, WAV_RATE, 0);
}
-TEST_CASE("[AudioStreamWAV] Saving IMA ADPCM is not supported") {
+TEST_CASE("[Audio][AudioStreamWAV] Saving IMA ADPCM is not supported") {
String save_path = TestUtils::get_temp_path("test_adpcm.wav");
Ref<AudioStreamWAV> stream = memnew(AudioStreamWAV);
stream->set_format(AudioStreamWAV::FORMAT_IMA_ADPCM);
diff --git a/tests/scene/test_text_edit.h b/tests/scene/test_text_edit.h
index 69e27fe7a0..46a5046b21 100644
--- a/tests/scene/test_text_edit.h
+++ b/tests/scene/test_text_edit.h
@@ -4232,6 +4232,18 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
CHECK(text_edit->get_caret_line(0) == 0);
CHECK(text_edit->get_caret_column(0) == 4);
text_edit->remove_secondary_carets();
+
+ // Remove when there are no words, only symbols.
+ text_edit->set_text("#{}");
+ text_edit->set_caret_line(0);
+ text_edit->set_caret_column(3);
+
+ SEND_GUI_ACTION("ui_text_backspace_word");
+ CHECK(text_edit->get_viewport()->is_input_handled());
+ CHECK_FALSE(text_edit->has_selection());
+ CHECK(text_edit->get_text() == "");
+ CHECK(text_edit->get_caret_line(0) == 0);
+ CHECK(text_edit->get_caret_column(0) == 0);
}
SUBCASE("[TextEdit] ui_text_backspace_word same line") {
@@ -4891,6 +4903,18 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
CHECK(text_edit->get_caret_line(0) == 0);
CHECK(text_edit->get_caret_column(0) == 2);
text_edit->remove_secondary_carets();
+
+ // Remove when there are no words, only symbols.
+ text_edit->set_text("#{}");
+ text_edit->set_caret_line(0);
+ text_edit->set_caret_column(0);
+
+ SEND_GUI_ACTION("ui_text_delete_word");
+ CHECK(text_edit->get_viewport()->is_input_handled());
+ CHECK_FALSE(text_edit->has_selection());
+ CHECK(text_edit->get_text() == "");
+ CHECK(text_edit->get_caret_line(0) == 0);
+ CHECK(text_edit->get_caret_column(0) == 0);
}
SUBCASE("[TextEdit] ui_text_delete_word same line") {
@@ -5301,6 +5325,16 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
SIGNAL_CHECK("caret_changed", empty_signal_args);
SIGNAL_CHECK_FALSE("text_changed");
SIGNAL_CHECK_FALSE("lines_edited_from");
+
+ // Move when there are no words, only symbols.
+ text_edit->set_text("#{}");
+ text_edit->set_caret_line(0);
+ text_edit->set_caret_column(3);
+
+ SEND_GUI_ACTION("ui_text_caret_word_left");
+ CHECK(text_edit->get_viewport()->is_input_handled());
+ CHECK(text_edit->get_caret_line(0) == 0);
+ CHECK(text_edit->get_caret_column(0) == 0);
}
SUBCASE("[TextEdit] ui_text_caret_left") {
@@ -5563,6 +5597,16 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
SIGNAL_CHECK("caret_changed", empty_signal_args);
SIGNAL_CHECK_FALSE("text_changed");
SIGNAL_CHECK_FALSE("lines_edited_from");
+
+ // Move when there are no words, only symbols.
+ text_edit->set_text("#{}");
+ text_edit->set_caret_line(0);
+ text_edit->set_caret_column(0);
+
+ SEND_GUI_ACTION("ui_text_caret_word_right");
+ CHECK(text_edit->get_viewport()->is_input_handled());
+ CHECK(text_edit->get_caret_line(0) == 0);
+ CHECK(text_edit->get_caret_column(0) == 3);
}
SUBCASE("[TextEdit] ui_text_caret_right") {
diff --git a/tests/test_main.cpp b/tests/test_main.cpp
index 7e1c431a3c..502aed6a6e 100644
--- a/tests/test_main.cpp
+++ b/tests/test_main.cpp
@@ -320,7 +320,7 @@ struct GodotTestCaseListener : public doctest::IReporter {
return;
}
- if (name.contains("Audio")) {
+ if (name.contains("[Audio]")) {
// The last driver index should always be the dummy driver.
int dummy_idx = AudioDriverManager::get_driver_count() - 1;
AudioDriverManager::initialize(dummy_idx);