Browse Source

Merge pull request #56537 from akien-mga/3.x-cherrypicks

Rémi Verschelde 3 years ago
parent
commit
ec64c6b656

+ 1 - 1
core/io/http_client.cpp

@@ -555,7 +555,7 @@ Error HTTPClient::poll() {
 							continue;
 						}
 						if (s.begins_with("content-length:")) {
-							body_size = s.substr(s.find(":") + 1, s.length()).strip_edges().to_int();
+							body_size = s.substr(s.find(":") + 1, s.length()).strip_edges().to_int64();
 							body_left = body_size;
 
 						} else if (s.begins_with("transfer-encoding:")) {

+ 2 - 2
core/io/http_client.h

@@ -180,8 +180,8 @@ private:
 	Vector<uint8_t> chunk;
 	int chunk_left;
 	bool chunk_trailer_part;
-	int body_size;
-	int body_left;
+	int64_t body_size;
+	int64_t body_left;
 	bool read_until_eof;
 
 	Ref<StreamPeerTCP> tcp_connection;

+ 1 - 1
doc/classes/ConvexPolygonShape2D.xml

@@ -20,7 +20,7 @@
 	</methods>
 	<members>
 		<member name="points" type="PoolVector2Array" setter="set_points" getter="get_points" default="PoolVector2Array(  )">
-			The polygon's list of vertices. Can be in either clockwise or counterclockwise order.
+			The polygon's list of vertices. Can be in either clockwise or counterclockwise order. Only set this property with convex hull points, use [method set_point_cloud] to generate a convex hull shape from concave shape points.
 		</member>
 	</members>
 	<constants>

+ 7 - 1
doc/classes/Input.xml

@@ -215,6 +215,7 @@
 				Returns [code]true[/code] when the user starts pressing the action event, meaning it's [code]true[/code] only on the frame that the user pressed down the button.
 				This is useful for code that needs to run only once when an action is pressed, instead of every frame while it's pressed.
 				If [code]exact[/code] is [code]false[/code], it ignores the input modifiers for [InputEventKey] and [InputEventMouseButton] events, and the direction for [InputEventJoypadMotion] events.
+				[b]Note:[/b] Due to keyboard ghosting, [method is_action_just_pressed] may return [code]false[/code] even if one of the action's keys is pressed. See [url=$DOCS_URL/tutorials/inputs/input_examples.html#keyboard-events]Input examples[/url] in the documentation for more information.
 			</description>
 		</method>
 		<method name="is_action_just_released" qualifiers="const">
@@ -233,6 +234,7 @@
 			<description>
 				Returns [code]true[/code] if you are pressing the action event. Note that if an action has multiple buttons assigned and more than one of them is pressed, releasing one button will release the action, even if some other button assigned to this action is still pressed.
 				If [code]exact[/code] is [code]false[/code], it ignores the input modifiers for [InputEventKey] and [InputEventMouseButton] events, and the direction for [InputEventJoypadMotion] events.
+				[b]Note:[/b] Due to keyboard ghosting, [method is_action_pressed] may return [code]false[/code] even if one of the action's keys is pressed. See [url=$DOCS_URL/tutorials/inputs/input_examples.html#keyboard-events]Input examples[/url] in the documentation for more information.
 			</description>
 		</method>
 		<method name="is_joy_button_pressed" qualifiers="const">
@@ -254,7 +256,9 @@
 			<return type="bool" />
 			<argument index="0" name="scancode" type="int" />
 			<description>
-				Returns [code]true[/code] if you are pressing the key. You can pass a [enum KeyList] constant.
+				Returns [code]true[/code] if you are pressing the key in the current keyboard layout. You can pass a [enum KeyList] constant.
+				[method is_key_pressed] is only recommended over [method is_physical_key_pressed] in non-game applications. This ensures that shortcut keys behave as expected depending on the user's keyboard layout, as keyboard shortcuts are generally dependent on the keyboard layout in non-game applications. If in doubt, use [method is_physical_key_pressed].
+				[b]Note:[/b] Due to keyboard ghosting, [method is_key_pressed] may return [code]false[/code] even if one of the action's keys is pressed. See [url=$DOCS_URL/tutorials/inputs/input_examples.html#keyboard-events]Input examples[/url] in the documentation for more information.
 			</description>
 		</method>
 		<method name="is_mouse_button_pressed" qualifiers="const">
@@ -269,6 +273,8 @@
 			<argument index="0" name="scancode" type="int" />
 			<description>
 				Returns [code]true[/code] if you are pressing the key in the physical location on the 101/102-key US QWERTY keyboard. You can pass a [enum KeyList] constant.
+				[method is_physical_key_pressed] is recommended over [method is_key_pressed] for in-game actions, as it will make W/A/S/D layouts work regardless of the user's keyboard layout. [method is_physical_key_pressed] will also ensure that the top row number keys work on any keyboard layout. If in doubt, use [method is_physical_key_pressed].
+				[b]Note:[/b] Due to keyboard ghosting, [method is_physical_key_pressed] may return [code]false[/code] even if one of the action's keys is pressed. See [url=$DOCS_URL/tutorials/inputs/input_examples.html#keyboard-events]Input examples[/url] in the documentation for more information.
 			</description>
 		</method>
 		<method name="joy_connection_changed">

+ 2 - 0
doc/classes/InputEvent.xml

@@ -53,6 +53,7 @@
 			<description>
 				Returns [code]true[/code] if the given action is being pressed (and is not an echo event for [InputEventKey] events, unless [code]allow_echo[/code] is [code]true[/code]). Not relevant for events of type [InputEventMouseMotion] or [InputEventScreenDrag].
 				If [code]exact_match[/code] is [code]false[/code], it ignores the input modifiers for [InputEventKey] and [InputEventMouseButton] events, and the direction for [InputEventJoypadMotion] events.
+				[b]Note:[/b] Due to keyboard ghosting, [method is_action_pressed] may return [code]false[/code] even if one of the action's keys is pressed. See [url=$DOCS_URL/tutorials/inputs/input_examples.html#keyboard-events]Input examples[/url] in the documentation for more information.
 			</description>
 		</method>
 		<method name="is_action_released" qualifiers="const">
@@ -80,6 +81,7 @@
 			<return type="bool" />
 			<description>
 				Returns [code]true[/code] if this input event is pressed. Not relevant for events of type [InputEventMouseMotion] or [InputEventScreenDrag].
+				[b]Note:[/b] Due to keyboard ghosting, [method is_action_pressed] may return [code]false[/code] even if one of the action's keys is pressed. See [url=$DOCS_URL/tutorials/inputs/input_examples.html#keyboard-events]Input examples[/url] in the documentation for more information.
 			</description>
 		</method>
 		<method name="shortcut_match" qualifiers="const">

+ 2 - 2
doc/classes/InputEventMouse.xml

@@ -16,10 +16,10 @@
 			The mouse button mask identifier, one of or a bitwise combination of the [enum ButtonList] button masks.
 		</member>
 		<member name="global_position" type="Vector2" setter="set_global_position" getter="get_global_position" default="Vector2( 0, 0 )">
-			The global mouse position relative to the current [Viewport] when used in [method Control._gui_input], otherwise is at 0,0.
+			The global mouse position relative to the current [Viewport]. If used in [method Control._gui_input] and if the current [Control] is not under the mouse, moving it will not update this value.
 		</member>
 		<member name="position" type="Vector2" setter="set_position" getter="get_position" default="Vector2( 0, 0 )">
-			The local mouse position relative to the [Viewport]. If used in [method Control._gui_input], the position is relative to the current [Control] which is under the mouse.
+			The local mouse position relative to the [Viewport]. If used in [method Control._gui_input], the position is relative to the current [Control] which is under the mouse. If the current [Control] is not under the mouse, moving it will not update this value.
 		</member>
 	</members>
 	<constants>

+ 1 - 1
doc/classes/ItemList.xml

@@ -121,7 +121,7 @@
 		<method name="get_v_scroll">
 			<return type="VScrollBar" />
 			<description>
-				Returns the [Object] ID associated with the list.
+				Returns the vertical scrollbar.
 				[b]Warning:[/b] This is a required internal node, removing and freeing it may cause a crash. If you wish to hide it or any of its children, use their [member CanvasItem.visible] property.
 			</description>
 		</method>

+ 1 - 1
doc/classes/OS.xml

@@ -427,7 +427,7 @@
 		<method name="get_static_memory_usage" qualifiers="const">
 			<return type="int" />
 			<description>
-				Returns the amount of static memory being used by the program in bytes.
+				Returns the amount of static memory being used by the program in bytes (only works in debug).
 			</description>
 		</method>
 		<method name="get_system_dir" qualifiers="const">

+ 1 - 1
doc/classes/Vector2.xml

@@ -217,7 +217,7 @@
 			<return type="Vector2" />
 			<argument index="0" name="n" type="Vector2" />
 			<description>
-				Returns the vector reflected from a plane defined by the given normal.
+				Returns the vector reflected (i.e. mirrored, or symmetric) over a line defined by the given direction vector [code]n[/code].
 			</description>
 		</method>
 		<method name="rotated">

+ 2 - 2
editor/editor_asset_installer.cpp

@@ -124,7 +124,7 @@ void EditorAssetInstaller::open(const String &p_path, int p_depth) {
 		char fname[16384];
 		unzGetCurrentFileInfo(pkg, &info, fname, 16384, nullptr, 0, nullptr, 0);
 
-		String name = fname;
+		String name = String::utf8(fname);
 		files_sorted.insert(name);
 
 		ret = unzGoToNextFile(pkg);
@@ -304,7 +304,7 @@ void EditorAssetInstaller::ok_pressed() {
 		char fname[16384];
 		ret = unzGetCurrentFileInfo(pkg, &info, fname, 16384, nullptr, 0, nullptr, 0);
 
-		String name = fname;
+		String name = String::utf8(fname);
 
 		if (status_map.has(name) && status_map[name]->is_checked(0)) {
 			String path = status_map[name]->get_metadata(0);

+ 13 - 5
editor/editor_node.cpp

@@ -1977,11 +1977,19 @@ static bool overrides_external_editor(Object *p_object) {
 	return script->get_language()->overrides_external_editor();
 }
 
-void EditorNode::_edit_current() {
+void EditorNode::_edit_current(bool p_skip_foreign) {
 	uint32_t current = editor_history.get_current();
 	Object *current_obj = current > 0 ? ObjectDB::get_instance(current) : nullptr;
-	bool inspector_only = editor_history.is_current_inspector_only();
 
+	RES res = Object::cast_to<Resource>(current_obj);
+	if (p_skip_foreign && res.is_valid()) {
+		if (res->get_path().find("::") > -1 && res->get_path().get_slice("::", 0) != editor_data.get_scene_path(get_current_tab())) {
+			// Trying to edit resource that belongs to another scene; abort.
+			current_obj = nullptr;
+		}
+	}
+
+	bool inspector_only = editor_history.is_current_inspector_only();
 	this->current = current_obj;
 
 	if (!current_obj) {
@@ -2112,8 +2120,8 @@ void EditorNode::_edit_current() {
 
 		if (main_plugin) {
 			// special case if use of external editor is true
-			Resource *res = Object::cast_to<Resource>(current_obj);
-			if (main_plugin->get_name() == "Script" && current_obj->get_class_name() != StringName("VisualScript") && res && !res->get_path().empty() && res->get_path().find("::") == -1 && (bool(EditorSettings::get_singleton()->get("text_editor/external/use_external_editor")) || overrides_external_editor(current_obj))) {
+			Resource *current_res = Object::cast_to<Resource>(current_obj);
+			if (main_plugin->get_name() == "Script" && current_obj->get_class_name() != StringName("VisualScript") && current_res && !current_res->get_path().empty() && current_res->get_path().find("::") == -1 && (bool(EditorSettings::get_singleton()->get("text_editor/external/use_external_editor")) || overrides_external_editor(current_obj))) {
 				if (!changing_scene) {
 					main_plugin->edit(current_obj);
 				}
@@ -3475,7 +3483,7 @@ void EditorNode::set_current_scene(int p_idx) {
 	}
 
 	Dictionary state = editor_data.restore_edited_scene_state(editor_selection, &editor_history);
-	_edit_current();
+	_edit_current(true);
 
 	_update_title();
 

+ 1 - 1
editor/editor_node.h

@@ -452,7 +452,7 @@ private:
 
 	void _dialog_action(String p_file);
 
-	void _edit_current();
+	void _edit_current(bool p_skip_foreign = false);
 	void _dialog_display_save_error(String p_file, Error p_error);
 	void _dialog_display_load_error(String p_file, Error p_error);
 

+ 26 - 0
editor/editor_properties.cpp

@@ -2139,6 +2139,29 @@ void EditorPropertyNodePath::_node_clear() {
 	update_property();
 }
 
+bool EditorPropertyNodePath::can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const {
+	return !is_read_only() && is_drop_valid(p_data);
+}
+
+void EditorPropertyNodePath::drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) {
+	ERR_FAIL_COND(!is_drop_valid(p_data));
+	Dictionary data = p_data;
+	Array nodes = data["nodes"];
+	Node *node = get_tree()->get_edited_scene_root()->get_node(nodes[0]);
+
+	if (node) {
+		_node_selected(node->get_path());
+	}
+}
+
+bool EditorPropertyNodePath::is_drop_valid(const Dictionary &p_drag_data) const {
+	if (p_drag_data["type"] != "nodes") {
+		return false;
+	}
+	Array nodes = p_drag_data["nodes"];
+	return nodes.size() == 1;
+}
+
 void EditorPropertyNodePath::update_property() {
 	NodePath p = get_edited_object()->get(get_edited_property());
 
@@ -2196,6 +2219,8 @@ void EditorPropertyNodePath::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("_node_selected"), &EditorPropertyNodePath::_node_selected);
 	ClassDB::bind_method(D_METHOD("_node_assign"), &EditorPropertyNodePath::_node_assign);
 	ClassDB::bind_method(D_METHOD("_node_clear"), &EditorPropertyNodePath::_node_clear);
+	ClassDB::bind_method(D_METHOD("_can_drop_data_fw", "position", "data", "from"), &EditorPropertyNodePath::can_drop_data_fw);
+	ClassDB::bind_method(D_METHOD("_drop_data_fw", "position", "data", "from"), &EditorPropertyNodePath::drop_data_fw);
 }
 
 EditorPropertyNodePath::EditorPropertyNodePath() {
@@ -2206,6 +2231,7 @@ EditorPropertyNodePath::EditorPropertyNodePath() {
 	assign->set_h_size_flags(SIZE_EXPAND_FILL);
 	assign->set_clip_text(true);
 	assign->connect("pressed", this, "_node_assign");
+	assign->set_drag_forwarding(this);
 	hbc->add_child(assign);
 
 	clear = memnew(Button);

+ 4 - 0
editor/editor_properties.h

@@ -522,6 +522,10 @@ class EditorPropertyNodePath : public EditorProperty {
 	void _node_assign();
 	void _node_clear();
 
+	bool can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const;
+	void drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from);
+	bool is_drop_valid(const Dictionary &p_drag_data) const;
+
 protected:
 	static void _bind_methods();
 	void _notification(int p_what);

+ 3 - 3
editor/export_template_manager.cpp

@@ -404,7 +404,7 @@ bool ExportTemplateManager::_install_file_selected(const String &p_file, bool p_
 		char fname[16384];
 		ret = unzGetCurrentFileInfo(pkg, &info, fname, 16384, nullptr, 0, nullptr, 0);
 
-		String file = fname;
+		String file = String::utf8(fname);
 		if (file.ends_with("version.txt")) {
 			Vector<uint8_t> data;
 			data.resize(info.uncompressed_size);
@@ -465,7 +465,7 @@ bool ExportTemplateManager::_install_file_selected(const String &p_file, bool p_
 		char fname[16384];
 		unzGetCurrentFileInfo(pkg, &info, fname, 16384, nullptr, 0, nullptr, 0);
 
-		String file_path(String(fname).simplify_path());
+		String file_path(String::utf8(fname).simplify_path());
 
 		String file = file_path.get_file();
 
@@ -711,7 +711,7 @@ Error ExportTemplateManager::install_android_template_from_file(const String &p_
 		char fpath[16384];
 		ret = unzGetCurrentFileInfo(pkg, &info, fpath, 16384, nullptr, 0, nullptr, 0);
 
-		String path = fpath;
+		String path = String::utf8(fpath);
 		String base_dir = path.get_base_dir();
 
 		if (!path.ends_with("/")) {

+ 25 - 5
editor/import_dock.cpp

@@ -31,6 +31,7 @@
 #include "import_dock.h"
 #include "editor_node.h"
 #include "editor_resource_preview.h"
+#include "editor_scale.h"
 
 class ImportDockParameters : public Object {
 	GDCLASS(ImportDockParameters, Object);
@@ -133,6 +134,8 @@ void ImportDock::set_edit_path(const String &p_path) {
 	_set_dirty(false);
 	import_as->set_disabled(false);
 	preset->set_disabled(false);
+	content->show();
+	select_a_resource->hide();
 
 	imported->set_text(p_path.get_file());
 }
@@ -397,6 +400,8 @@ void ImportDock::clear() {
 	params->properties.clear();
 	params->update();
 	preset->get_popup()->clear();
+	content->hide();
+	select_a_resource->show();
 }
 
 static bool _find_owners(EditorFileSystemDirectory *efsd, const String &p_path) {
@@ -573,12 +578,18 @@ void ImportDock::initialize_import_options() const {
 
 ImportDock::ImportDock() {
 	set_name("Import");
+
+	content = memnew(VBoxContainer);
+	content->set_v_size_flags(SIZE_EXPAND_FILL);
+	add_child(content);
+	content->hide();
+
 	imported = memnew(Label);
 	imported->add_style_override("normal", EditorNode::get_singleton()->get_gui_base()->get_stylebox("normal", "LineEdit"));
 	imported->set_clip_text(true);
-	add_child(imported);
+	content->add_child(imported);
 	HBoxContainer *hb = memnew(HBoxContainer);
-	add_margin_child(TTR("Import As:"), hb);
+	content->add_margin_child(TTR("Import As:"), hb);
 	import_as = memnew(OptionButton);
 	import_as->set_disabled(true);
 	import_as->connect("item_selected", this, "_importer_selected");
@@ -591,13 +602,13 @@ ImportDock::ImportDock() {
 	hb->add_child(preset);
 
 	import_opts = memnew(EditorInspector);
-	add_child(import_opts);
+	content->add_child(import_opts);
 	import_opts->set_v_size_flags(SIZE_EXPAND_FILL);
 	import_opts->connect("property_edited", this, "_property_edited");
 	import_opts->connect("property_toggled", this, "_property_toggled");
 
 	hb = memnew(HBoxContainer);
-	add_child(hb);
+	content->add_child(hb);
 	import = memnew(Button);
 	import->set_text(TTR("Reimport"));
 	import->set_disabled(true);
@@ -608,7 +619,7 @@ ImportDock::ImportDock() {
 
 	reimport_confirm = memnew(ConfirmationDialog);
 	reimport_confirm->get_ok()->set_text(TTR("Save Scenes, Re-Import, and Restart"));
-	add_child(reimport_confirm);
+	content->add_child(reimport_confirm);
 	reimport_confirm->connect("confirmed", this, "_reimport_and_restart");
 
 	VBoxContainer *vbc_confirm = memnew(VBoxContainer());
@@ -618,6 +629,15 @@ ImportDock::ImportDock() {
 	reimport_confirm->add_child(vbc_confirm);
 
 	params = memnew(ImportDockParameters);
+
+	select_a_resource = memnew(Label);
+	select_a_resource->set_text(TTR("Select a resource file in the filesystem or in the inspector to adjust import settings."));
+	select_a_resource->set_autowrap(true);
+	select_a_resource->set_custom_minimum_size(Size2(100 * EDSCALE, 0));
+	select_a_resource->set_v_size_flags(SIZE_EXPAND_FILL);
+	select_a_resource->set_align(Label::ALIGN_CENTER);
+	select_a_resource->set_valign(Label::VALIGN_CENTER);
+	add_child(select_a_resource);
 }
 
 ImportDock::~ImportDock() {

+ 3 - 0
editor/import_dock.h

@@ -59,6 +59,9 @@ class ImportDock : public VBoxContainer {
 
 	ImportDockParameters *params;
 
+	VBoxContainer *content;
+	Label *select_a_resource;
+
 	void _preset_selected(int p_idx);
 	void _importer_selected(int i_idx);
 	void _update_options(const Ref<ConfigFile> &p_config = Ref<ConfigFile>());

+ 10 - 2
editor/plugins/canvas_item_editor_plugin.cpp

@@ -2287,8 +2287,16 @@ bool CanvasItemEditor::_gui_input_move(const Ref<InputEvent> &p_event) {
 	if (k.is_valid() && k->is_pressed() && (tool == TOOL_SELECT || tool == TOOL_MOVE) &&
 			(k->get_scancode() == KEY_UP || k->get_scancode() == KEY_DOWN || k->get_scancode() == KEY_LEFT || k->get_scancode() == KEY_RIGHT)) {
 		if (!k->is_echo()) {
-			// Start moving the canvas items with the keyboard
-			drag_selection = _get_edited_canvas_items();
+			// Start moving the canvas items with the keyboard, if they are movable
+			List<CanvasItem *> selection = _get_edited_canvas_items();
+
+			drag_selection.clear();
+			for (List<CanvasItem *>::Element *E = selection.front(); E; E = E->next()) {
+				if (_is_node_movable(E->get(), true)) {
+					drag_selection.push_back(E->get());
+				}
+			}
+
 			drag_type = DRAG_KEY_MOVE;
 			drag_from = Vector2();
 			drag_to = Vector2();

+ 3 - 3
editor/project_manager.cpp

@@ -204,7 +204,7 @@ private:
 						char fname[16384];
 						ret = unzGetCurrentFileInfo(pkg, &info, fname, 16384, nullptr, 0, nullptr, 0);
 
-						if (String(fname).ends_with("project.godot")) {
+						if (String::utf8(fname).ends_with("project.godot")) {
 							break;
 						}
 
@@ -514,7 +514,7 @@ private:
 						char fname[16384];
 						unzGetCurrentFileInfo(pkg, &info, fname, 16384, nullptr, 0, nullptr, 0);
 
-						String name = fname;
+						String name = String::utf8(fname);
 						if (name.ends_with("project.godot")) {
 							zip_root = name.substr(0, name.rfind("project.godot"));
 							break;
@@ -534,7 +534,7 @@ private:
 						char fname[16384];
 						ret = unzGetCurrentFileInfo(pkg, &info, fname, 16384, nullptr, 0, nullptr, 0);
 
-						String path = fname;
+						String path = String::utf8(fname);
 
 						if (path == String() || path == zip_root || !zip_root.is_subsequence_of(path)) {
 							//

+ 18 - 12
editor/scene_tree_dock.cpp

@@ -228,7 +228,7 @@ void SceneTreeDock::_perform_instance_scenes(const Vector<String> &p_files, Node
 	}
 
 	editor_data->get_undo_redo().commit_action();
-	editor->push_item(instances[instances.size() - 1]);
+	_push_item(instances[instances.size() - 1]);
 	for (int i = 0; i < instances.size(); i++) {
 		emit_signal("node_created", instances[i]);
 	}
@@ -748,7 +748,7 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
 			editor_data->get_undo_redo().commit_action();
 
 			if (dupsingle) {
-				editor->push_item(dupsingle);
+				_push_item(dupsingle);
 			}
 		} break;
 		case TOOL_REPARENT: {
@@ -853,7 +853,7 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
 				mne->add_node(root->get_path_to(E->key()));
 			}
 
-			EditorNode::get_singleton()->push_item(mne.ptr());
+			_push_item(mne.ptr());
 
 		} break;
 
@@ -1172,7 +1172,7 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
 				Object *obj = ObjectDB::get_instance(subresources[idx]);
 				ERR_FAIL_COND(!obj);
 
-				editor->push_item(obj);
+				_push_item(obj);
 			}
 		}
 	}
@@ -1362,6 +1362,12 @@ void SceneTreeDock::_script_open_request(const Ref<Script> &p_script) {
 	editor->edit_resource(p_script);
 }
 
+void SceneTreeDock::_push_item(Object *p_object) {
+	if (!Input::get_singleton()->is_key_pressed(KEY_ALT)) {
+		editor->push_item(p_object);
+	}
+}
+
 void SceneTreeDock::_node_selected() {
 	Node *node = scene_tree->get_selected();
 
@@ -1373,7 +1379,7 @@ void SceneTreeDock::_node_selected() {
 		restore_script_editor_on_drag = true;
 	}
 
-	editor->push_item(node);
+	_push_item(node);
 }
 
 void SceneTreeDock::_node_renamed() {
@@ -1944,7 +1950,7 @@ void SceneTreeDock::_script_created(Ref<Script> p_script) {
 
 	editor_data->get_undo_redo().commit_action();
 
-	editor->push_item(p_script.operator->());
+	_push_item(p_script.operator->());
 	_update_script_button();
 }
 
@@ -2073,7 +2079,7 @@ void SceneTreeDock::_delete_confirm(bool p_cut) {
 		editor->get_viewport_control()->update();
 	}
 
-	editor->push_item(nullptr);
+	_push_item(nullptr);
 
 	// Fixes the EditorHistory from still offering deleted notes
 	EditorHistory *editor_history = EditorNode::get_singleton()->get_editor_history();
@@ -2117,9 +2123,9 @@ void SceneTreeDock::_selection_changed() {
 		//automatically turn on multi-edit
 		_tool_selected(TOOL_MULTI_EDIT);
 	} else if (selection_size == 1) {
-		editor->push_item(editor_selection->get_selection().front()->key());
+		_push_item(editor_selection->get_selection().front()->key());
 	} else if (selection_size == 0) {
-		editor->push_item(nullptr);
+		_push_item(nullptr);
 	}
 
 	_update_script_button();
@@ -2172,7 +2178,7 @@ void SceneTreeDock::_do_create(Node *p_parent) {
 	}
 
 	editor_data->get_undo_redo().commit_action();
-	editor->push_item(c);
+	_push_item(c);
 	editor_selection->clear();
 	editor_selection->add_node(child);
 	if (Object::cast_to<Control>(c)) {
@@ -2328,7 +2334,7 @@ void SceneTreeDock::replace_node(Node *p_node, Node *p_by_node, bool p_keep_prop
 		memdelete(default_oldnode);
 	}
 
-	editor->push_item(nullptr);
+	_push_item(nullptr);
 
 	//reconnect signals
 	List<MethodInfo> sl;
@@ -2373,7 +2379,7 @@ void SceneTreeDock::replace_node(Node *p_node, Node *p_by_node, bool p_keep_prop
 	}
 	newnode->set_name(newname);
 
-	editor->push_item(newnode);
+	_push_item(newnode);
 
 	if (p_remove_old) {
 		memdelete(n);

+ 1 - 0
editor/scene_tree_dock.h

@@ -194,6 +194,7 @@ class SceneTreeDock : public VBoxContainer {
 	void _node_replace_owner(Node *p_base, Node *p_node, Node *p_root, ReplaceOwnerMode p_mode = MODE_BIDI);
 	void _load_request(const String &p_path);
 	void _script_open_request(const Ref<Script> &p_script);
+	void _push_item(Object *p_object);
 
 	bool _cyclical_dependency_exists(const String &p_target_scene_path, Node *p_desired_node);
 	bool _track_inherit(const String &p_target_scene_path, Node *p_desired_node);

+ 24 - 21
modules/gltf/gltf_document.cpp

@@ -3382,30 +3382,33 @@ Error GLTFDocument::_serialize_materials(Ref<GLTFState> state) {
 			tex.instance();
 			{
 				Ref<Texture> normal_texture = material->get_texture(SpatialMaterial::TEXTURE_NORMAL);
-				// Code for uncompressing RG normal maps
-				Ref<Image> img = normal_texture->get_data();
-				Ref<ImageTexture> img_tex = img;
-				if (img_tex.is_valid()) {
-					img = img_tex->get_data();
-				}
-				img->decompress();
-				img->convert(Image::FORMAT_RGBA8);
-				img->lock();
-				for (int32_t y = 0; y < img->get_height(); y++) {
-					for (int32_t x = 0; x < img->get_width(); x++) {
-						Color c = img->get_pixel(x, y);
-						Vector2 red_green = Vector2(c.r, c.g);
-						red_green = red_green * Vector2(2.0f, 2.0f) - Vector2(1.0f, 1.0f);
-						float blue = 1.0f - red_green.dot(red_green);
-						blue = MAX(0.0f, blue);
-						c.b = Math::sqrt(blue);
-						img->set_pixel(x, y, c);
+				if (normal_texture.is_valid()) {
+					// Code for uncompressing RG normal maps
+					Ref<Image> img = normal_texture->get_data();
+					if (img.is_valid()) {
+						Ref<ImageTexture> img_tex = img;
+						if (img_tex.is_valid()) {
+							img = img_tex->get_data();
+						}
+						img->decompress();
+						img->convert(Image::FORMAT_RGBA8);
+						img->lock();
+						for (int32_t y = 0; y < img->get_height(); y++) {
+							for (int32_t x = 0; x < img->get_width(); x++) {
+								Color c = img->get_pixel(x, y);
+								Vector2 red_green = Vector2(c.r, c.g);
+								red_green = red_green * Vector2(2.0f, 2.0f) - Vector2(1.0f, 1.0f);
+								float blue = 1.0f - red_green.dot(red_green);
+								blue = MAX(0.0f, blue);
+								c.b = Math::sqrt(blue);
+								img->set_pixel(x, y, c);
+							}
+						}
+						img->unlock();
+						tex->create_from_image(img);
 					}
 				}
-				img->unlock();
-				tex->create_from_image(img);
 			}
-			Ref<Texture> normal_texture = material->get_texture(SpatialMaterial::TEXTURE_NORMAL);
 			GLTFTextureIndex gltf_texture_index = -1;
 			if (tex.is_valid() && tex->get_data().is_valid()) {
 				tex->set_name(material->get_name() + "_normal");

+ 2 - 4
modules/mono/glue/collections_glue.cpp

@@ -236,10 +236,8 @@ int32_t godot_icall_Dictionary_KeyValuePairs(Dictionary *ptr, Array **keys, Arra
 }
 
 void godot_icall_Dictionary_KeyValuePairAt(Dictionary *ptr, int index, MonoObject **key, MonoObject **value) {
-	Array *keys = godot_icall_Dictionary_Keys(ptr);
-	Array *values = godot_icall_Dictionary_Values(ptr);
-	*key = GDMonoMarshal::variant_to_mono_object(keys->get(index));
-	*value = GDMonoMarshal::variant_to_mono_object(values->get(index));
+	*key = GDMonoMarshal::variant_to_mono_object(ptr->get_key_at_index(index));
+	*value = GDMonoMarshal::variant_to_mono_object(ptr->get_value_at_index(index));
 }
 
 void godot_icall_Dictionary_Add(Dictionary *ptr, MonoObject *key, MonoObject *value) {

+ 2 - 2
platform/android/export/export_plugin.cpp

@@ -3114,7 +3114,7 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP
 
 		bool skip = false;
 
-		String file = fname;
+		String file = String::utf8(fname);
 
 		Vector<uint8_t> data;
 		data.resize(info.uncompressed_size);
@@ -3297,7 +3297,7 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP
 		char extra[16384];
 		ret = unzGetCurrentFileInfo(tmp_unaligned, &info, fname, 16384, extra, 16384 - ZIP_ALIGNMENT, nullptr, 0);
 
-		String file = fname;
+		String file = String::utf8(fname);
 
 		Vector<uint8_t> data;
 		data.resize(info.compressed_size);

+ 10 - 2
platform/android/java/build.gradle

@@ -124,8 +124,11 @@ task zipCustomBuild(type: Zip) {
 def templateExcludedBuildTask() {
     // We exclude these gradle tasks so we can run the scons command manually.
     def excludedTasks = []
-    for (String buildType : supportedTargets) {
-        excludedTasks += ":lib:" + getSconsTaskName(buildType)
+    if (!isAndroidStudio()) {
+        logger.lifecycle("Excluding Android studio build tasks")
+        for (String buildType : supportedTargets) {
+            excludedTasks += ":lib:" + getSconsTaskName(buildType)
+        }
     }
     return excludedTasks
 }
@@ -155,6 +158,11 @@ def templateBuildTasks() {
     return tasks
 }
 
+def isAndroidStudio() {
+    def sysProps = System.getProperties()
+    return sysProps != null && sysProps['idea.platform.prefix'] != null
+}
+
 /**
  * Master task used to coordinate the tasks defined above to generate the set of Godot templates.
  */

+ 1 - 1
platform/iphone/export/export.cpp

@@ -1755,7 +1755,7 @@ Error EditorExportPlatformIOS::export_project(const Ref<EditorExportPreset> &p_p
 		char fname[16384];
 		ret = unzGetCurrentFileInfo(src_pkg_zip, &info, fname, 16384, nullptr, 0, nullptr, 0);
 
-		String file = fname;
+		String file = String::utf8(fname);
 
 		print_line("READ: " + file);
 		Vector<uint8_t> data;

+ 1 - 1
platform/javascript/export/export.cpp

@@ -375,7 +375,7 @@ Error EditorExportPlatformJavaScript::_extract_template(const String &p_template
 		char fname[16384];
 		unzGetCurrentFileInfo(pkg, &info, fname, 16384, nullptr, 0, nullptr, 0);
 
-		String file = fname;
+		String file = String::utf8(fname);
 
 		// Skip service worker and offline page if not exporting pwa.
 		if (!pwa && (file == "godot.service.worker.js" || file == "godot.offline.html")) {

+ 1 - 1
platform/osx/export/export.cpp

@@ -657,7 +657,7 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p
 		char fname[16384];
 		ret = unzGetCurrentFileInfo(src_pkg_zip, &info, fname, 16384, nullptr, 0, nullptr, 0);
 
-		String file = fname;
+		String file = String::utf8(fname);
 
 		Vector<uint8_t> data;
 		data.resize(info.uncompressed_size);

+ 3 - 3
platform/uwp/export/export.cpp

@@ -1265,10 +1265,10 @@ public:
 		while (ret == UNZ_OK) {
 			// get file name
 			unz_file_info info;
-			char fname[16834];
-			ret = unzGetCurrentFileInfo(pkg, &info, fname, 16834, nullptr, 0, nullptr, 0);
+			char fname[16384];
+			ret = unzGetCurrentFileInfo(pkg, &info, fname, 16384, nullptr, 0, nullptr, 0);
 
-			String path = fname;
+			String path = String::utf8(fname);
 
 			if (path.ends_with("/")) {
 				// Ignore directories

+ 1 - 1
scene/gui/control.cpp

@@ -472,7 +472,7 @@ void Control::_notification(int p_notification) {
 		} break;
 		case NOTIFICATION_EXIT_TREE: {
 			ERR_FAIL_COND(!get_viewport());
-
+			release_focus();
 			get_viewport()->_gui_remove_control(this);
 
 		} break;

+ 6 - 17
scene/gui/tab_container.cpp

@@ -557,28 +557,16 @@ void TabContainer::add_child_notify(Node *p_child) {
 		return;
 	}
 
-	bool first = false;
+	call_deferred("_repaint");
+	update();
 
-	if (get_tab_count() != 1) {
-		c->hide();
-	} else {
-		c->show();
-		//call_deferred("set_current_tab",0);
-		first = true;
+	bool first = (get_tab_count() == 1);
+
+	if (first) {
 		current = 0;
 		previous = 0;
 	}
-	c->set_anchors_and_margins_preset(Control::PRESET_WIDE);
-	if (tabs_visible) {
-		c->set_margin(MARGIN_TOP, _get_top_margin());
-	}
-	Ref<StyleBox> sb = get_stylebox("panel");
-	c->set_margin(MARGIN_TOP, c->get_margin(MARGIN_TOP) + sb->get_margin(MARGIN_TOP));
-	c->set_margin(MARGIN_LEFT, c->get_margin(MARGIN_LEFT) + sb->get_margin(MARGIN_LEFT));
-	c->set_margin(MARGIN_RIGHT, c->get_margin(MARGIN_RIGHT) - sb->get_margin(MARGIN_RIGHT));
-	c->set_margin(MARGIN_BOTTOM, c->get_margin(MARGIN_BOTTOM) - sb->get_margin(MARGIN_BOTTOM));
 
-	update();
 	p_child->connect("renamed", this, "_child_renamed_callback");
 	if (first) {
 		emit_signal("tab_changed", current);
@@ -1069,6 +1057,7 @@ void TabContainer::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("get_use_hidden_tabs_for_min_size"), &TabContainer::get_use_hidden_tabs_for_min_size);
 
 	ClassDB::bind_method(D_METHOD("_child_renamed_callback"), &TabContainer::_child_renamed_callback);
+	ClassDB::bind_method(D_METHOD("_repaint"), &TabContainer::_repaint);
 	ClassDB::bind_method(D_METHOD("_on_theme_changed"), &TabContainer::_on_theme_changed);
 	ClassDB::bind_method(D_METHOD("_on_mouse_exited"), &TabContainer::_on_mouse_exited);
 	ClassDB::bind_method(D_METHOD("_update_current_tab"), &TabContainer::_update_current_tab);

+ 1 - 1
scene/gui/tree.cpp

@@ -3868,7 +3868,7 @@ void Tree::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("get_allow_reselect"), &Tree::get_allow_reselect);
 
 	ADD_PROPERTY(PropertyInfo(Variant::INT, "columns"), "set_columns", "get_columns");
-	ADD_PROPERTY(PropertyInfo(Variant::INT, "column_titles_visible"), "set_column_titles_visible", "are_column_titles_visible");
+	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "column_titles_visible"), "set_column_titles_visible", "are_column_titles_visible");
 	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "allow_reselect"), "set_allow_reselect", "get_allow_reselect");
 	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "allow_rmb_select"), "set_allow_rmb_select", "get_allow_rmb_select");
 	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "hide_folding"), "set_hide_folding", "is_folding_hidden");

+ 1 - 0
scene/resources/animation.cpp

@@ -1870,6 +1870,7 @@ void Animation::value_track_set_update_mode(int p_track, UpdateMode p_mode) {
 
 	ValueTrack *vt = static_cast<ValueTrack *>(t);
 	vt->update_mode = p_mode;
+	emit_changed();
 }
 
 Animation::UpdateMode Animation::value_track_get_update_mode(int p_track) const {

+ 1 - 1
scene/resources/texture.cpp

@@ -463,7 +463,7 @@ Error StreamTexture::_load_data(const String &p_path, int &tw, int &th, int &tw_
 	ERR_FAIL_COND_V(image.is_null(), ERR_INVALID_PARAMETER);
 
 	FileAccess *f = FileAccess::open(p_path, FileAccess::READ);
-	ERR_FAIL_COND_V(!f, ERR_CANT_OPEN);
+	ERR_FAIL_COND_V_MSG(!f, ERR_CANT_OPEN, vformat("Unable to open file: %s.", p_path));
 
 	uint8_t header[4];
 	f->get_buffer(header, 4);