From 3bdb39bec454d6a03a04e9d3712b812e6987950d Mon Sep 17 00:00:00 2001 From: Hugo Locurcio Date: Sat, 15 Feb 2020 02:18:24 +0100 Subject: [PATCH 01/38] Add a margin to EditorSpinSlider to visually line up the edited number This means clicking on an EditorSpinSlider to edit its value will no longer cause the number to be visually offset while it's being edited. (cherry picked from commit cc615fee5f88d543fb5877ddd0207b832772f7fe) --- editor/editor_spin_slider.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/editor/editor_spin_slider.cpp b/editor/editor_spin_slider.cpp index 74984cef03b..64e6f874a60 100644 --- a/editor/editor_spin_slider.cpp +++ b/editor/editor_spin_slider.cpp @@ -31,6 +31,7 @@ #include "editor_spin_slider.h" #include "core/math/expression.h" #include "core/os/input.h" +#include "editor_node.h" #include "editor_scale.h" String EditorSpinSlider::get_tooltip(const Point2 &p_pos) const { @@ -203,6 +204,19 @@ void EditorSpinSlider::_notification(int p_what) { } } + if (p_what == NOTIFICATION_READY) { + // Add a left margin to the stylebox to make the number align with the Label + // when it's edited. The LineEdit "focus" stylebox uses the "normal" stylebox's + // default margins. + Ref stylebox = + EditorNode::get_singleton()->get_theme_base()->get_stylebox("normal", "LineEdit")->duplicate(); + // EditorSpinSliders with a label have more space on the left, so add an + // higher margin to match the location where the text begins. + // The margin values below were determined by empirical testing. + stylebox->set_default_margin(MARGIN_LEFT, (get_label() != String() ? 23 : 16) * EDSCALE); + value_input->add_style_override("normal", stylebox); + } + if (p_what == NOTIFICATION_DRAW) { updown_offset = -1; From f189d0e1f308cfe1e4cea23a104f34f119768d29 Mon Sep 17 00:00:00 2001 From: "K. S. Ernest (iFire) Lee" Date: Fri, 15 May 2020 10:17:53 -0700 Subject: [PATCH 02/38] Allow gltf2 morph targets with no default values. Changes for bug 38751 (cherry picked from commit 71ae0ff46d75e0e94cc2ef839b0bc82db2bfc7de) --- editor/import/editor_scene_importer_gltf.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/editor/import/editor_scene_importer_gltf.cpp b/editor/import/editor_scene_importer_gltf.cpp index 03e609eca7b..c6e919512fa 100644 --- a/editor/import/editor_scene_importer_gltf.cpp +++ b/editor/import/editor_scene_importer_gltf.cpp @@ -1230,10 +1230,14 @@ Error EditorSceneImporterGLTF::_parse_meshes(GLTFState &state) { } } + mesh.blend_weights.resize(mesh.mesh->get_blend_shape_count()); + for (int32_t weight_i = 0; weight_i < mesh.blend_weights.size(); weight_i++) { + mesh.blend_weights.write[weight_i] = 0.0f; + } + if (d.has("weights")) { const Array &weights = d["weights"]; - ERR_FAIL_COND_V(mesh.mesh->get_blend_shape_count() != weights.size(), ERR_PARSE_ERROR); - mesh.blend_weights.resize(weights.size()); + ERR_FAIL_COND_V(mesh.blend_weights.size() != weights.size(), ERR_PARSE_ERROR); for (int j = 0; j < weights.size(); j++) { mesh.blend_weights.write[j] = weights[j]; } From 582b4318f8c6fd8e5000f6804a0bb452f21da4c4 Mon Sep 17 00:00:00 2001 From: rileylyman Date: Tue, 26 May 2020 21:49:05 -0700 Subject: [PATCH 03/38] implement generic filename disambiguation A static function is added to EditorNode which allows for filename disambiguation given a list of filenames and the corresponding list of absolute paths for those files. This function is then used to disambiguate scene and script tabs in the editor. (cherry picked from commit 4285211f40cf3519237faf08b8fdbf09bc28e954) --- editor/editor_node.cpp | 117 +++++++++++++++++++++++- editor/editor_node.h | 2 + editor/plugins/script_editor_plugin.cpp | 13 +++ 3 files changed, 131 insertions(+), 1 deletion(-) diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index 9746d4be2a5..b19ccbefc20 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -175,12 +175,127 @@ EditorNode *EditorNode::singleton = NULL; +void EditorNode::disambiguate_filenames(const Vector p_full_paths, Vector &r_filenames) { + // Keep track of a list of "index sets," i.e. sets of indices + // within disambiguated_scene_names which contain the same name. + Vector > index_sets; + Map scene_name_to_set_index; + for (int i = 0; i < r_filenames.size(); i++) { + String scene_name = r_filenames[i]; + if (!scene_name_to_set_index.has(scene_name)) { + index_sets.push_back(Set()); + scene_name_to_set_index.insert(r_filenames[i], index_sets.size() - 1); + } + index_sets.write[scene_name_to_set_index[scene_name]].insert(i); + } + + // For each index set with a size > 1, we need to disambiguate + for (int i = 0; i < index_sets.size(); i++) { + Set iset = index_sets[i]; + while (iset.size() > 1) { + // Append the parent folder to each scene name + for (Set::Element *E = iset.front(); E; E = E->next()) { + int set_idx = E->get(); + String scene_name = r_filenames[set_idx]; + String full_path = p_full_paths[set_idx]; + + // Get rid of file extensions and res:// prefixes + if (scene_name.rfind(".") >= 0) { + scene_name = scene_name.substr(0, scene_name.rfind(".")); + } + if (full_path.begins_with("res://")) { + full_path = full_path.substr(6); + } + if (full_path.rfind(".") >= 0) { + full_path = full_path.substr(0, full_path.rfind(".")); + } + + int scene_name_size = scene_name.size(); + int full_path_size = full_path.size(); + int difference = full_path_size - scene_name_size; + + // Find just the parent folder of the current path and append it. + // If the current name is foo.tscn, and the full path is /some/folder/foo.tscn + // then slash_idx is the second '/', so that we select just "folder", and + // append that to yield "folder/foo.tscn". + if (difference > 0) { + String parent = full_path.substr(0, difference); + int slash_idx = parent.rfind("/"); + slash_idx = parent.rfind("/", slash_idx - 1); + parent = slash_idx >= 0 ? parent.substr(slash_idx + 1) : parent; + r_filenames.write[set_idx] = parent + r_filenames[set_idx]; + } + } + + // Loop back through scene names and remove non-ambiguous names + bool can_proceed = false; + Set::Element *E = iset.front(); + while (E) { + String scene_name = r_filenames[E->get()]; + bool duplicate_found = false; + for (Set::Element *F = iset.front(); F; F = F->next()) { + if (E->get() == F->get()) { + continue; + } + String other_scene_name = r_filenames[F->get()]; + if (other_scene_name == scene_name) { + duplicate_found = true; + break; + } + } + + Set::Element *to_erase = duplicate_found ? nullptr : E; + + // We need to check that we could actually append anymore names + // if we wanted to for disambiguation. If we can't, then we have + // to abort even with ambiguous names. We clean the full path + // and the scene name first to remove extensions so that this + // comparison actually works. + String path = p_full_paths[E->get()]; + if (path.begins_with("res://")) { + path = path.substr(6); + } + if (path.rfind(".") >= 0) { + path = path.substr(0, path.rfind(".")); + } + if (scene_name.rfind(".") >= 0) { + scene_name = scene_name.substr(0, scene_name.rfind(".")); + } + + // We can proceed iff the full path is longer than the scene name, + // meaning that there is at least one more parent folder we can + // tack onto the name. + can_proceed = can_proceed || (path.size() - scene_name.size()) >= 1; + + E = E->next(); + if (to_erase) { + iset.erase(to_erase); + } + } + + if (!can_proceed) { + break; + } + } + } +} + void EditorNode::_update_scene_tabs() { bool show_rb = EditorSettings::get_singleton()->get("interface/scene_tabs/show_script_button"); OS::get_singleton()->global_menu_clear("_dock"); + // Get all scene names, which may be ambiguous + Vector disambiguated_scene_names; + Vector full_path_names; + for (int i = 0; i < editor_data.get_edited_scene_count(); i++) { + disambiguated_scene_names.push_back(editor_data.get_scene_title(i)); + full_path_names.push_back(editor_data.get_scene_path(i)); + } + + disambiguate_filenames(full_path_names, disambiguated_scene_names); + scene_tabs->clear_tabs(); Ref script_icon = gui_base->get_icon("Script", "EditorIcons"); for (int i = 0; i < editor_data.get_edited_scene_count(); i++) { @@ -193,7 +308,7 @@ void EditorNode::_update_scene_tabs() { int current = editor_data.get_edited_scene(); bool unsaved = (i == current) ? saved_version != editor_data.get_undo_redo().get_version() : editor_data.get_scene_version(i) != 0; - scene_tabs->add_tab(editor_data.get_scene_title(i) + (unsaved ? "(*)" : ""), icon); + scene_tabs->add_tab(disambiguated_scene_names[i] + (unsaved ? "(*)" : ""), icon); OS::get_singleton()->global_menu_add_item("_dock", editor_data.get_scene_title(i) + (unsaved ? "(*)" : ""), GLOBAL_SCENE, i); diff --git a/editor/editor_node.h b/editor/editor_node.h index 1ff879ce3c1..e5708a8e212 100644 --- a/editor/editor_node.h +++ b/editor/editor_node.h @@ -685,6 +685,8 @@ public: static void add_editor_plugin(EditorPlugin *p_editor, bool p_config_changed = false); static void remove_editor_plugin(EditorPlugin *p_editor, bool p_config_changed = false); + static void disambiguate_filenames(const Vector p_full_paths, Vector &r_filenames); + void new_inherited_scene() { _menu_option_confirm(FILE_NEW_INHERITED_SCENE, false); } void set_docks_visible(bool p_show); diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp index 99c3d87168f..65a95d7e289 100644 --- a/editor/plugins/script_editor_plugin.cpp +++ b/editor/plugins/script_editor_plugin.cpp @@ -1887,6 +1887,19 @@ void ScriptEditor::_update_script_names() { sedata.push_back(sd); } + Vector disambiguated_script_names; + Vector full_script_paths; + for (int j = 0; j < sedata.size(); j++) { + disambiguated_script_names.push_back(sedata[j].name); + full_script_paths.push_back(sedata[j].tooltip); + } + + EditorNode::disambiguate_filenames(full_script_paths, disambiguated_script_names); + + for (int j = 0; j < sedata.size(); j++) { + sedata.write[j].name = disambiguated_script_names[j]; + } + EditorHelp *eh = Object::cast_to(tab_container->get_child(i)); if (eh) { From 8ef40b930641baa0206528ed7461334c5227758a Mon Sep 17 00:00:00 2001 From: Chistpohe LY Date: Wed, 24 Jun 2020 12:26:18 +0200 Subject: [PATCH 04/38] bug with Tween.is_active, fixes #39760 (cherry picked from commit d60617de101dbcde31ac67c34022a2153910d065) --- scene/animation/tween.cpp | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/scene/animation/tween.cpp b/scene/animation/tween.cpp index 795accd8507..96523981965 100644 --- a/scene/animation/tween.cpp +++ b/scene/animation/tween.cpp @@ -689,7 +689,7 @@ void Tween::_tween_process(float p_delta) { } // Are all of the tweens complete? - bool all_finished = true; + int any_unfinished = 0; // For each tween we wish to interpolate... for (List::Element *E = interpolates.front(); E; E = E->next()) { @@ -697,13 +697,13 @@ void Tween::_tween_process(float p_delta) { // Get the data from it InterpolateData &data = E->get(); - // Track if we hit one that isn't finished yet - all_finished = all_finished && data.finish; - // Is the data not active or already finished? No need to go any further if (!data.active || data.finish) continue; + // Track if we hit one that isn't finished yet + any_unfinished++; + // Get the target object for this interpolation Object *object = ObjectDB::get_instance(data.id); if (object == NULL) @@ -787,18 +787,17 @@ void Tween::_tween_process(float p_delta) { emit_signal("tween_completed", object, NodePath(Vector(), data.key, false)); // If we are not repeating the tween, remove it - if (!repeat) + if (!repeat) { call_deferred("_remove_by_uid", data.uid); - } else if (!repeat) { - // Check whether all tweens are finished - all_finished = all_finished && data.finish; + any_unfinished--; + } } } // One less update left to go pending_update--; // If all tweens are completed, we no longer need to be active - if (all_finished) { + if (any_unfinished == 0) { set_active(false); emit_signal("tween_all_completed"); } From b7b8f8645fa1051be4cc009f2f0e6ab4d89fee07 Mon Sep 17 00:00:00 2001 From: Stijn Hinlopen Date: Mon, 29 Jun 2020 01:04:28 +0200 Subject: [PATCH 05/38] Open scene selected from Quick Open dialog. (cherry picked from commit 1ce3a77a42fc92c278466c30b56b43673941c769) --- editor/editor_node.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index b19ccbefc20..e3d923dd5e7 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -3705,10 +3705,14 @@ void EditorNode::_quick_opened() { Vector files = quick_open->get_selected_files(); + bool open_scene_dialog = quick_open->get_base_type() == "PackedScene"; for (int i = 0; i < files.size(); i++) { String res_path = files[i]; - if (quick_open->get_base_type() == "PackedScene") { + List scene_extensions; + ResourceLoader::get_recognized_extensions_for_type("PackedScene", &scene_extensions); + + if (open_scene_dialog || scene_extensions.find(files[i].get_extension())) { open_request(res_path); } else { load_resource(res_path); From dc7e9d46e66199b4065875f9723ae621e7465dda Mon Sep 17 00:00:00 2001 From: Umang Kalra Date: Thu, 9 Jul 2020 01:32:11 +0530 Subject: [PATCH 06/38] Fixes the get_visible_line_count() of rich text label (cherry picked from commit bd32c273ff4e2357e1fd7a8613241332252623e9) --- scene/gui/rich_text_label.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp index 43185abb195..88125087da9 100644 --- a/scene/gui/rich_text_label.cpp +++ b/scene/gui/rich_text_label.cpp @@ -1021,8 +1021,8 @@ void RichTextLabel::_notification(int p_what) { visible_line_count = 0; while (y < size.height && from_line < main->lines.size()) { - - visible_line_count += _process_line(main, text_rect.get_position(), y, text_rect.get_size().width - scroll_w, from_line, PROCESS_DRAW, base_font, base_color, font_color_shadow, use_outline, shadow_ofs, Point2i(), NULL, NULL, NULL, total_chars); + visible_line_count++; + _process_line(main, text_rect.get_position(), y, text_rect.get_size().width - scroll_w, from_line, PROCESS_DRAW, base_font, base_color, font_color_shadow, use_outline, shadow_ofs, Point2i(), NULL, NULL, NULL, total_chars); total_chars += main->lines[from_line].char_count; from_line++; From 11bceb3d62986d4106b63a5da674838e78185e72 Mon Sep 17 00:00:00 2001 From: Hugo Locurcio Date: Wed, 15 Jul 2020 10:34:47 +0200 Subject: [PATCH 07/38] Document caveats related to Control scaling This is a common topic of confusion. Clarifying its intended scope should make things easier to understand. (cherry picked from commit a36912b3cbba6e24ae562ec5ab67fc51202bd56f) --- doc/classes/Control.xml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/doc/classes/Control.xml b/doc/classes/Control.xml index f99aaff0ae9..cb8bb064249 100644 --- a/doc/classes/Control.xml +++ b/doc/classes/Control.xml @@ -864,7 +864,9 @@ The node's rotation around its pivot, in degrees. See [member rect_pivot_offset] to change the pivot's position. - The node's scale, relative to its [member rect_size]. Change this property to scale the node around its [member rect_pivot_offset]. + The node's scale, relative to its [member rect_size]. Change this property to scale the node around its [member rect_pivot_offset]. The Control's [member hint_tooltip] will also scale according to this value. + [b]Note:[/b] This property is mainly intended to be used for animation purposes. Text inside the Control will look pixelated or blurry when the Control is scaled. To support multiple resolutions in your project, use an appropriate viewport stretch mode as described in the [url=https://docs.godotengine.org/en/latest/tutorials/viewports/multiple_resolutions.html]documentation[/url] instead of scaling Controls individually. + [b]Note:[/b] If the Control node is a child of a [Container] node, the scale will be reset to [code]Vector2(1, 1)[/code] when the scene is instanced. To set the Control's scale when it's instanced, wait for one frame using [code]yield(get_tree(), "idle_frame")[/code] then set its [member rect_scale] property. The size of the node's bounding rectangle, in pixels. [Container] nodes update this property automatically. From 5c7802a06129aa4a646acbdf6dc225cf6c7d9db0 Mon Sep 17 00:00:00 2001 From: Hugo Locurcio Date: Wed, 15 Jul 2020 11:48:54 +0200 Subject: [PATCH 08/38] Document which escape sequences are supported by `String.c_unescape()` See https://github.com/godotengine/godot/issues/38716. (cherry picked from commit 04b25108ac3c2d4ded4006a685c98b2028899123) --- doc/classes/String.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/classes/String.xml b/doc/classes/String.xml index c66f16ae07e..3aa56a0e502 100644 --- a/doc/classes/String.xml +++ b/doc/classes/String.xml @@ -253,7 +253,8 @@ - Returns a copy of the string with escaped characters replaced by their meanings according to the C language standard. + Returns a copy of the string with escaped characters replaced by their meanings. Supported escape sequences are [code]\'[/code], [code]\"[/code], [code]\?[/code], [code]\\[/code], [code]\a[/code], [code]\b[/code], [code]\f[/code], [code]\n[/code], [code]\r[/code], [code]\t[/code], [code]\v[/code]. + [b]Note:[/b] Unlike the GDScript parser, this method doesn't support the [code]\uXXXX[/code] escape sequence. From c77492408af0ab3e30dbf0a117a38371db86a605 Mon Sep 17 00:00:00 2001 From: Ryan Roden-Corrent Date: Wed, 15 Jul 2020 08:09:27 -0400 Subject: [PATCH 09/38] Clarify how to convert PrimitiveMesh to ArrayMesh. It took me a bit to figure this out, as I was initially doing something more complicated like this before I realized I just had to pass get_mesh_arrays directly to add_surface_from_arrays. ``` var arr_mesh = ArrayMesh.new() var arrays = [] arrays.resize(ArrayMesh.ARRAY_MAX) arrays[ArrayMesh.ARRAY_VERTEX] = c.get_mesh_arrays() arr_mesh.add_surface_from_arrays(Mesh.PRIMITIVE_TRIANGLES, arrays) ``` (cherry picked from commit 933bf9652385c7e63418bc52f2c8e2bb2311d577) --- doc/classes/PrimitiveMesh.xml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/doc/classes/PrimitiveMesh.xml b/doc/classes/PrimitiveMesh.xml index e2c6b921e63..44ec10470bf 100644 --- a/doc/classes/PrimitiveMesh.xml +++ b/doc/classes/PrimitiveMesh.xml @@ -13,7 +13,12 @@ - Returns mesh arrays used to constitute surface of [Mesh]. Mesh arrays can be used with [ArrayMesh] to create new surfaces. + Returns mesh arrays used to constitute surface of [Mesh]. The result can be passed to [method ArrayMesh.add_surface_from_arrays] to create a new surface. For example: + [codeblock] + var c := CylinderMesh.new() + var arr_mesh := ArrayMesh.new() + arr_mesh.add_surface_from_arrays(Mesh.PRIMITIVE_TRIANGLES, c.get_mesh_arrays()) + [/codeblock] From 753ff1fad276acdc83f0bd935e58ae67c004f3b4 Mon Sep 17 00:00:00 2001 From: Hugo Locurcio Date: Wed, 15 Jul 2020 16:10:06 +0200 Subject: [PATCH 10/38] Mention the Data paths documentation in the File class This closes https://github.com/godotengine/godot-docs/issues/3799. (cherry picked from commit d4085d6bfb3ae8e3db79db2d76f77213e576d2a6) --- doc/classes/File.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/classes/File.xml b/doc/classes/File.xml index dbba0267460..a6a435ff43f 100644 --- a/doc/classes/File.xml +++ b/doc/classes/File.xml @@ -20,6 +20,7 @@ file.close() return content [/codeblock] + In the example above, the file will be saved in the user data folder as specified in the [url=https://docs.godotengine.org/en/latest/tutorials/io/data_paths.html]Data paths[/url] documentation. https://docs.godotengine.org/en/latest/getting_started/step_by_step/filesystem.html From ca4fe82a7f57c5e74665aa4af0c0c5dafead7e4e Mon Sep 17 00:00:00 2001 From: Paulb23 Date: Thu, 16 Jul 2020 15:50:23 +0100 Subject: [PATCH 11/38] Fix crash when closing a TextFile (cherry picked from commit 54bca425b2443cc8e3fd7299cd8108bcad1729b4) --- editor/plugins/script_editor_plugin.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp index 65a95d7e289..56e1b9d2ee6 100644 --- a/editor/plugins/script_editor_plugin.cpp +++ b/editor/plugins/script_editor_plugin.cpp @@ -585,7 +585,7 @@ void ScriptEditor::_close_tab(int p_idx, bool p_save, bool p_history_back) { Ref