diff options
-rw-r--r-- | doc/classes/CanvasItem.xml | 2 | ||||
-rw-r--r-- | doc/classes/RenderingServer.xml | 1 | ||||
-rw-r--r-- | drivers/gles3/rasterizer_scene_gles3.cpp | 2 | ||||
-rw-r--r-- | editor/animation_bezier_editor.cpp | 4 | ||||
-rw-r--r-- | editor/editor_file_system.cpp | 46 | ||||
-rw-r--r-- | editor/editor_file_system.h | 3 | ||||
-rw-r--r-- | editor/editor_node.cpp | 9 | ||||
-rw-r--r-- | editor/editor_node.h | 3 | ||||
-rw-r--r-- | editor/gui/editor_file_dialog.cpp | 3 | ||||
-rw-r--r-- | editor/gui/scene_tree_editor.cpp | 49 | ||||
-rw-r--r-- | editor/gui/scene_tree_editor.h | 9 | ||||
-rw-r--r-- | editor/plugins/visual_shader_editor_plugin.cpp | 2 | ||||
-rw-r--r-- | editor/scene_tree_dock.cpp | 1 | ||||
-rw-r--r-- | platform/windows/display_server_windows.cpp | 106 | ||||
-rw-r--r-- | platform/windows/display_server_windows.h | 10 | ||||
-rw-r--r-- | scene/gui/file_dialog.cpp | 3 | ||||
-rw-r--r-- | scene/gui/line_edit.cpp | 52 | ||||
-rw-r--r-- | scene/gui/text_edit.cpp | 19 | ||||
-rw-r--r-- | servers/rendering/renderer_rd/environment/sky.cpp | 2 | ||||
-rw-r--r-- | tests/scene/test_audio_stream_wav.h | 18 | ||||
-rw-r--r-- | tests/scene/test_text_edit.h | 44 | ||||
-rw-r--r-- | tests/test_main.cpp | 2 |
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); |