summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/io/image.cpp5
-rw-r--r--core/io/image.h4
-rw-r--r--doc/classes/Label.xml2
-rw-r--r--doc/classes/NodePath.xml1
-rw-r--r--editor/editor_node.cpp5
-rw-r--r--editor/editor_node.h1
-rw-r--r--editor/scene_tree_dock.cpp163
-rw-r--r--editor/scene_tree_dock.h3
-rw-r--r--modules/pvr/image_compress_pvrtc.cpp4
-rw-r--r--platform/javascript/export/export.cpp2
-rw-r--r--platform/javascript/js/engine/config.js10
-rw-r--r--platform/javascript/js/libs/library_godot_os.js3
-rw-r--r--scene/gui/graph_node.cpp2
-rw-r--r--scene/resources/packed_scene.cpp4
14 files changed, 151 insertions, 58 deletions
diff --git a/core/io/image.cpp b/core/io/image.cpp
index 3e79b7efbc..3112dd217f 100644
--- a/core/io/image.cpp
+++ b/core/io/image.cpp
@@ -2380,6 +2380,8 @@ Error Image::decompress() {
}
Error Image::compress(CompressMode p_mode, CompressSource p_source, float p_lossy_quality) {
+ ERR_FAIL_INDEX_V_MSG(p_mode, COMPRESS_MAX, ERR_INVALID_PARAMETER, "Invalid compress mode.");
+ ERR_FAIL_INDEX_V_MSG(p_source, COMPRESS_SOURCE_MAX, ERR_INVALID_PARAMETER, "Invalid compress source.");
return compress_from_channels(p_mode, detect_used_channels(p_source), p_lossy_quality);
}
@@ -2405,6 +2407,9 @@ Error Image::compress_from_channels(CompressMode p_mode, UsedChannels p_channels
ERR_FAIL_COND_V(!_image_compress_bptc_func, ERR_UNAVAILABLE);
_image_compress_bptc_func(this, p_lossy_quality, p_channels);
} break;
+ case COMPRESS_MAX: {
+ ERR_FAIL_V(ERR_INVALID_PARAMETER);
+ } break;
}
return OK;
diff --git a/core/io/image.h b/core/io/image.h
index 060e54a308..8f1b251ac3 100644
--- a/core/io/image.h
+++ b/core/io/image.h
@@ -336,11 +336,13 @@ public:
COMPRESS_ETC,
COMPRESS_ETC2,
COMPRESS_BPTC,
+ COMPRESS_MAX,
};
enum CompressSource {
COMPRESS_SOURCE_GENERIC,
COMPRESS_SOURCE_SRGB,
- COMPRESS_SOURCE_NORMAL
+ COMPRESS_SOURCE_NORMAL,
+ COMPRESS_SOURCE_MAX,
};
Error compress(CompressMode p_mode, CompressSource p_source = COMPRESS_SOURCE_GENERIC, float p_lossy_quality = 0.7);
diff --git a/doc/classes/Label.xml b/doc/classes/Label.xml
index ee59f0c85a..0789ac9010 100644
--- a/doc/classes/Label.xml
+++ b/doc/classes/Label.xml
@@ -79,7 +79,7 @@
If [code]true[/code], wraps the text inside the node's bounding rectangle. If you resize the node, it will change its height automatically to show all the text.
</member>
<member name="clip_text" type="bool" setter="set_clip_text" getter="is_clipping_text" default="false">
- If [code]true[/code], the Label only shows the text that fits inside its bounding rectangle. It also lets you scale the node down freely.
+ If [code]true[/code], the Label only shows the text that fits inside its bounding rectangle and will clip text horizontally.
</member>
<member name="language" type="String" setter="set_language" getter="get_language" default="&quot;&quot;">
Language code used for line-breaking and text shaping algorithms, if left empty current locale is used instead.
diff --git a/doc/classes/NodePath.xml b/doc/classes/NodePath.xml
index 817ccd5160..0ba2e73ad4 100644
--- a/doc/classes/NodePath.xml
+++ b/doc/classes/NodePath.xml
@@ -20,6 +20,7 @@
@"/root/Main" # If your main scene's root node were named "Main".
@"/root/MyAutoload" # If you have an autoloaded node or scene.
[/codeblock]
+ [b]Note:[/b] In the editor, [NodePath] properties are automatically updated when moving, renaming or deleting a node in the scene tree, but they are never updated at runtime.
</description>
<tutorials>
<link title="2D Role Playing Game Demo">https://godotengine.org/asset-library/asset/520</link>
diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp
index b962ea6e3e..1bc04676b7 100644
--- a/editor/editor_node.cpp
+++ b/editor/editor_node.cpp
@@ -709,6 +709,7 @@ void EditorNode::_notification(int p_what) {
p->set_item_icon(p->get_item_index(HELP_DOCS), gui_base->get_theme_icon("Instance", "EditorIcons"));
p->set_item_icon(p->get_item_index(HELP_QA), gui_base->get_theme_icon("Instance", "EditorIcons"));
p->set_item_icon(p->get_item_index(HELP_REPORT_A_BUG), gui_base->get_theme_icon("Instance", "EditorIcons"));
+ p->set_item_icon(p->get_item_index(HELP_SUGGEST_A_FEATURE), gui_base->get_theme_icon("Instance", "EditorIcons"));
p->set_item_icon(p->get_item_index(HELP_SEND_DOCS_FEEDBACK), gui_base->get_theme_icon("Instance", "EditorIcons"));
p->set_item_icon(p->get_item_index(HELP_COMMUNITY), gui_base->get_theme_icon("Instance", "EditorIcons"));
p->set_item_icon(p->get_item_index(HELP_ABOUT), gui_base->get_theme_icon("Godot", "EditorIcons"));
@@ -2796,6 +2797,9 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) {
case HELP_REPORT_A_BUG: {
OS::get_singleton()->shell_open("https://github.com/godotengine/godot/issues");
} break;
+ case HELP_SUGGEST_A_FEATURE: {
+ OS::get_singleton()->shell_open("https://github.com/godotengine/godot-proposals#readme");
+ } break;
case HELP_SEND_DOCS_FEEDBACK: {
OS::get_singleton()->shell_open("https://github.com/godotengine/godot-docs/issues");
} break;
@@ -6361,6 +6365,7 @@ EditorNode::EditorNode() {
p->add_icon_shortcut(gui_base->get_theme_icon("Instance", "EditorIcons"), ED_SHORTCUT("editor/online_docs", TTR("Online Documentation")), HELP_DOCS);
p->add_icon_shortcut(gui_base->get_theme_icon("Instance", "EditorIcons"), ED_SHORTCUT("editor/q&a", TTR("Questions & Answers")), HELP_QA);
p->add_icon_shortcut(gui_base->get_theme_icon("Instance", "EditorIcons"), ED_SHORTCUT("editor/report_a_bug", TTR("Report a Bug")), HELP_REPORT_A_BUG);
+ p->add_icon_shortcut(gui_base->get_theme_icon("Instance", "EditorIcons"), ED_SHORTCUT("editor/suggest_a_feature", TTR("Suggest a Feature")), HELP_SUGGEST_A_FEATURE);
p->add_icon_shortcut(gui_base->get_theme_icon("Instance", "EditorIcons"), ED_SHORTCUT("editor/send_docs_feedback", TTR("Send Docs Feedback")), HELP_SEND_DOCS_FEEDBACK);
p->add_icon_shortcut(gui_base->get_theme_icon("Instance", "EditorIcons"), ED_SHORTCUT("editor/community", TTR("Community")), HELP_COMMUNITY);
p->add_separator();
diff --git a/editor/editor_node.h b/editor/editor_node.h
index 9a135cfdd7..dcb6ad6e94 100644
--- a/editor/editor_node.h
+++ b/editor/editor_node.h
@@ -194,6 +194,7 @@ private:
HELP_DOCS,
HELP_QA,
HELP_REPORT_A_BUG,
+ HELP_SUGGEST_A_FEATURE,
HELP_SEND_DOCS_FEEDBACK,
HELP_COMMUNITY,
HELP_ABOUT,
diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp
index 2114ca41d3..e63818ef07 100644
--- a/editor/scene_tree_dock.cpp
+++ b/editor/scene_tree_dock.cpp
@@ -1409,9 +1409,102 @@ void SceneTreeDock::fill_path_renames(Node *p_node, Node *p_new_parent, List<Pai
_fill_path_renames(base_path, new_base_path, p_node, p_renames);
}
+bool SceneTreeDock::_update_node_path(const NodePath &p_root_path, NodePath &p_node_path, List<Pair<NodePath, NodePath>> *p_renames) {
+ NodePath root_path_new = p_root_path;
+ for (List<Pair<NodePath, NodePath>>::Element *F = p_renames->front(); F; F = F->next()) {
+ if (p_root_path == F->get().first) {
+ root_path_new = F->get().second;
+ break;
+ }
+ }
+
+ // Goes through all paths to check if it's matching.
+ for (List<Pair<NodePath, NodePath>>::Element *F = p_renames->front(); F; F = F->next()) {
+ NodePath rel_path_old = p_root_path.rel_path_to(F->get().first);
+
+ // If old path detected, then it needs to be replaced with the new one.
+ if (p_node_path == rel_path_old) {
+ NodePath rel_path_new = F->get().second;
+
+ // If not empty, get new relative path.
+ if (!rel_path_new.is_empty()) {
+ rel_path_new = root_path_new.rel_path_to(rel_path_new);
+ }
+
+ p_node_path = rel_path_new;
+ return true;
+ }
+
+ // Update the node itself if it has a valid node path and has not been deleted.
+ if (p_root_path == F->get().first && p_node_path != NodePath() && F->get().second != NodePath()) {
+ NodePath abs_path = NodePath(String(root_path_new).plus_file(p_node_path)).simplified();
+ NodePath rel_path_new = F->get().second.rel_path_to(abs_path);
+
+ p_node_path = rel_path_new;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool SceneTreeDock::_check_node_path_recursive(const NodePath &p_root_path, Variant &p_variant, List<Pair<NodePath, NodePath>> *p_renames) {
+ switch (p_variant.get_type()) {
+ case Variant::NODE_PATH: {
+ NodePath node_path = p_variant;
+ if (_update_node_path(p_root_path, node_path, p_renames)) {
+ p_variant = node_path;
+ return true;
+ }
+ } break;
+
+ case Variant::ARRAY: {
+ Array a = p_variant;
+ bool updated = false;
+ for (int i = 0; i < a.size(); i++) {
+ Variant value = a[i];
+ if (_check_node_path_recursive(p_root_path, value, p_renames)) {
+ if (!updated) {
+ a = a.duplicate(); // Need to duplicate for undo-redo to work.
+ updated = true;
+ }
+ a[i] = value;
+ }
+ }
+ if (updated) {
+ p_variant = a;
+ return true;
+ }
+ } break;
+
+ case Variant::DICTIONARY: {
+ Dictionary d = p_variant;
+ bool updated = false;
+ for (int i = 0; i < d.size(); i++) {
+ Variant value = d.get_value_at_index(i);
+ if (_check_node_path_recursive(p_root_path, value, p_renames)) {
+ if (!updated) {
+ d = d.duplicate(); // Need to duplicate for undo-redo to work.
+ updated = true;
+ }
+ d[d.get_key_at_index(i)] = value;
+ }
+ }
+ if (updated) {
+ p_variant = d;
+ return true;
+ }
+ } break;
+
+ default: {
+ }
+ }
+
+ return false;
+}
+
void SceneTreeDock::perform_node_renames(Node *p_base, List<Pair<NodePath, NodePath>> *p_renames, Map<Ref<Animation>, Set<int>> *r_rem_anims) {
Map<Ref<Animation>, Set<int>> rem_anims;
-
if (!r_rem_anims) {
r_rem_anims = &rem_anims;
}
@@ -1424,60 +1517,22 @@ void SceneTreeDock::perform_node_renames(Node *p_base, List<Pair<NodePath, NodeP
return;
}
- // Renaming node paths used in script instances
- if (p_base->get_script_instance()) {
- ScriptInstance *si = p_base->get_script_instance();
-
- if (si) {
- List<PropertyInfo> properties;
- si->get_property_list(&properties);
- NodePath root_path = p_base->get_path();
-
- for (List<PropertyInfo>::Element *E = properties.front(); E; E = E->next()) {
- String propertyname = E->get().name;
- Variant p = p_base->get(propertyname);
- if (p.get_type() == Variant::NODE_PATH) {
- NodePath root_path_new = root_path;
- for (List<Pair<NodePath, NodePath>>::Element *F = p_renames->front(); F; F = F->next()) {
- if (root_path == F->get().first) {
- root_path_new = F->get().second;
- break;
- }
- }
-
- // Goes through all paths to check if its matching
- for (List<Pair<NodePath, NodePath>>::Element *F = p_renames->front(); F; F = F->next()) {
- NodePath rel_path_old = root_path.rel_path_to(F->get().first);
+ // Renaming node paths used in node properties.
+ List<PropertyInfo> properties;
+ p_base->get_property_list(&properties);
+ NodePath base_root_path = p_base->get_path();
- // if old path detected, then it needs to be replaced with the new one
- if (p == rel_path_old) {
- NodePath rel_path_new = F->get().second;
-
- // if not empty, get new relative path
- if (!rel_path_new.is_empty()) {
- rel_path_new = root_path_new.rel_path_to(F->get().second);
- }
-
- editor_data->get_undo_redo().add_do_property(p_base, propertyname, rel_path_new);
- editor_data->get_undo_redo().add_undo_property(p_base, propertyname, rel_path_old);
-
- p_base->set(propertyname, rel_path_new);
- break;
- }
-
- // update the node itself if it has a valid node path and has not been deleted
- if (root_path == F->get().first && p != NodePath() && F->get().second != NodePath()) {
- NodePath abs_path = NodePath(String(root_path).plus_file(p)).simplified();
- NodePath rel_path_new = F->get().second.rel_path_to(abs_path);
-
- editor_data->get_undo_redo().add_do_property(p_base, propertyname, rel_path_new);
- editor_data->get_undo_redo().add_undo_property(p_base, propertyname, p);
-
- p_base->set(propertyname, rel_path_new);
- }
- }
- }
- }
+ for (List<PropertyInfo>::Element *E = properties.front(); E; E = E->next()) {
+ if (!(E->get().usage & (PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_EDITOR))) {
+ continue;
+ }
+ String propertyname = E->get().name;
+ Variant old_variant = p_base->get(propertyname);
+ Variant updated_variant = old_variant;
+ if (_check_node_path_recursive(base_root_path, updated_variant, p_renames)) {
+ editor_data->get_undo_redo().add_do_property(p_base, propertyname, updated_variant);
+ editor_data->get_undo_redo().add_undo_property(p_base, propertyname, old_variant);
+ p_base->set(propertyname, updated_variant);
}
}
diff --git a/editor/scene_tree_dock.h b/editor/scene_tree_dock.h
index a313337540..d2a68ebd2e 100644
--- a/editor/scene_tree_dock.h
+++ b/editor/scene_tree_dock.h
@@ -247,6 +247,9 @@ class SceneTreeDock : public VBoxContainer {
static SceneTreeDock *singleton;
static void _update_configuration_warning();
+ static bool _update_node_path(const NodePath &p_root_path, NodePath &p_node_path, List<Pair<NodePath, NodePath>> *p_renames);
+ static bool _check_node_path_recursive(const NodePath &p_root_path, Variant &p_variant, List<Pair<NodePath, NodePath>> *p_renames);
+
protected:
void _notification(int p_what);
static void _bind_methods();
diff --git a/modules/pvr/image_compress_pvrtc.cpp b/modules/pvr/image_compress_pvrtc.cpp
index f33912cec0..980cac17d3 100644
--- a/modules/pvr/image_compress_pvrtc.cpp
+++ b/modules/pvr/image_compress_pvrtc.cpp
@@ -43,6 +43,10 @@ static void _compress_pvrtc1_4bpp(Image *p_img) {
if (!img->is_size_po2() || img->get_width() != img->get_height()) {
make_mipmaps = img->has_mipmaps();
img->resize_to_po2(true);
+ // Resizing can fail for some formats
+ if (!img->is_size_po2() || img->get_width() != img->get_height()) {
+ ERR_FAIL_MSG("Failed to resize the image for compression.");
+ }
}
img->convert(Image::FORMAT_RGBA8);
if (!img->has_mipmaps() && make_mipmaps) {
diff --git a/platform/javascript/export/export.cpp b/platform/javascript/export/export.cpp
index 51c72e10eb..bf4244eda4 100644
--- a/platform/javascript/export/export.cpp
+++ b/platform/javascript/export/export.cpp
@@ -450,6 +450,7 @@ void EditorExportPlatformJavaScript::_fix_html(Vector<uint8_t> &p_html, const Re
}
config["canvasResizePolicy"] = p_preset->get("html/canvas_resize_policy");
config["experimentalVK"] = p_preset->get("html/experimental_virtual_keyboard");
+ config["focusCanvas"] = p_preset->get("html/focus_canvas_on_start");
config["gdnativeLibs"] = libs;
config["executable"] = p_name;
config["args"] = args;
@@ -650,6 +651,7 @@ void EditorExportPlatformJavaScript::get_export_options(List<ExportOption> *r_op
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "html/custom_html_shell", PROPERTY_HINT_FILE, "*.html"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "html/head_include", PROPERTY_HINT_MULTILINE_TEXT), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "html/canvas_resize_policy", PROPERTY_HINT_ENUM, "None,Project,Adaptive"), 2));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "html/focus_canvas_on_start"), true));
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "html/experimental_virtual_keyboard"), false));
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "progressive_web_app/enabled"), false));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "progressive_web_app/offline_page", PROPERTY_HINT_FILE, "*.html"), ""));
diff --git a/platform/javascript/js/engine/config.js b/platform/javascript/js/engine/config.js
index 6072782875..ba61b14eb7 100644
--- a/platform/javascript/js/engine/config.js
+++ b/platform/javascript/js/engine/config.js
@@ -91,6 +91,14 @@ const InternalConfig = function (initConfig) { // eslint-disable-line no-unused-
*/
args: [],
/**
+ * When enabled, the game canvas will automatically grab the focus when the engine starts.
+ *
+ * @memberof EngineConfig
+ * @type {boolean}
+ * @default
+ */
+ focusCanvas: true,
+ /**
* When enabled, this will turn on experimental virtual keyboard support on mobile.
*
* @memberof EngineConfig
@@ -238,6 +246,7 @@ const InternalConfig = function (initConfig) { // eslint-disable-line no-unused-
this.persistentPaths = parse('persistentPaths', this.persistentPaths);
this.persistentDrops = parse('persistentDrops', this.persistentDrops);
this.experimentalVK = parse('experimentalVK', this.experimentalVK);
+ this.focusCanvas = parse('focusCanvas', this.focusCanvas);
this.gdnativeLibs = parse('gdnativeLibs', this.gdnativeLibs);
this.fileSizes = parse('fileSizes', this.fileSizes);
this.args = parse('args', this.args);
@@ -324,6 +333,7 @@ const InternalConfig = function (initConfig) { // eslint-disable-line no-unused-
'locale': locale,
'persistentDrops': this.persistentDrops,
'virtualKeyboard': this.experimentalVK,
+ 'focusCanvas': this.focusCanvas,
'onExecute': this.onExecute,
'onExit': function (p_code) {
cleanup(); // We always need to call the cleanup callback to free memory.
diff --git a/platform/javascript/js/libs/library_godot_os.js b/platform/javascript/js/libs/library_godot_os.js
index 7414b8cc47..5aa750757c 100644
--- a/platform/javascript/js/libs/library_godot_os.js
+++ b/platform/javascript/js/libs/library_godot_os.js
@@ -72,6 +72,9 @@ const GodotConfig = {
GodotConfig.persistent_drops = !!p_opts['persistentDrops'];
GodotConfig.on_execute = p_opts['onExecute'];
GodotConfig.on_exit = p_opts['onExit'];
+ if (p_opts['focusCanvas']) {
+ GodotConfig.canvas.focus();
+ }
},
locate_file: function (file) {
diff --git a/scene/gui/graph_node.cpp b/scene/gui/graph_node.cpp
index 93f1fe9e8e..836bffdf46 100644
--- a/scene/gui/graph_node.cpp
+++ b/scene/gui/graph_node.cpp
@@ -757,7 +757,7 @@ void GraphNode::_connpos_update() {
continue;
}
- Size2i size = c->get_combined_minimum_size();
+ Size2i size = c->get_rect().size;
int y = sb->get_margin(SIDE_TOP) + vofs;
int h = size.y;
diff --git a/scene/resources/packed_scene.cpp b/scene/resources/packed_scene.cpp
index 913f1ca376..9bb2a4ddb8 100644
--- a/scene/resources/packed_scene.cpp
+++ b/scene/resources/packed_scene.cpp
@@ -471,7 +471,9 @@ Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Map
StringName type = p_node->get_class();
Ref<Script> script = p_node->get_script();
- if (script.is_valid()) {
+ if (Engine::get_singleton()->is_editor_hint() && script.is_valid()) {
+ // Should be called in the editor only and not at runtime,
+ // otherwise it can cause problems because of missing instance state support.
script->update_exports();
}