浏览代码

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

Rémi Verschelde 4 年之前
父节点
当前提交
e0fb05ad30

+ 5 - 6
core/bind/core_bind.cpp

@@ -760,7 +760,7 @@ int64_t _OS::get_unix_time_from_datetime(Dictionary datetime) const {
 	unsigned int hour = ((datetime.has(HOUR_KEY)) ? static_cast<unsigned int>(datetime[HOUR_KEY]) : 0);
 	unsigned int hour = ((datetime.has(HOUR_KEY)) ? static_cast<unsigned int>(datetime[HOUR_KEY]) : 0);
 	unsigned int day = ((datetime.has(DAY_KEY)) ? static_cast<unsigned int>(datetime[DAY_KEY]) : 1);
 	unsigned int day = ((datetime.has(DAY_KEY)) ? static_cast<unsigned int>(datetime[DAY_KEY]) : 1);
 	unsigned int month = ((datetime.has(MONTH_KEY)) ? static_cast<unsigned int>(datetime[MONTH_KEY]) : 1);
 	unsigned int month = ((datetime.has(MONTH_KEY)) ? static_cast<unsigned int>(datetime[MONTH_KEY]) : 1);
-	unsigned int year = ((datetime.has(YEAR_KEY)) ? static_cast<unsigned int>(datetime[YEAR_KEY]) : 0);
+	unsigned int year = ((datetime.has(YEAR_KEY)) ? static_cast<unsigned int>(datetime[YEAR_KEY]) : 1970);
 
 
 	/// How many days come before each month (0-12)
 	/// How many days come before each month (0-12)
 	static const unsigned short int DAYS_PAST_THIS_YEAR_TABLE[2][13] = {
 	static const unsigned short int DAYS_PAST_THIS_YEAR_TABLE[2][13] = {
@@ -771,15 +771,14 @@ int64_t _OS::get_unix_time_from_datetime(Dictionary datetime) const {
 	};
 	};
 
 
 	ERR_FAIL_COND_V_MSG(second > 59, 0, "Invalid second value of: " + itos(second) + ".");
 	ERR_FAIL_COND_V_MSG(second > 59, 0, "Invalid second value of: " + itos(second) + ".");
-
 	ERR_FAIL_COND_V_MSG(minute > 59, 0, "Invalid minute value of: " + itos(minute) + ".");
 	ERR_FAIL_COND_V_MSG(minute > 59, 0, "Invalid minute value of: " + itos(minute) + ".");
-
 	ERR_FAIL_COND_V_MSG(hour > 23, 0, "Invalid hour value of: " + itos(hour) + ".");
 	ERR_FAIL_COND_V_MSG(hour > 23, 0, "Invalid hour value of: " + itos(hour) + ".");
-
+	ERR_FAIL_COND_V_MSG(year == 0, 0, "Years before 1 AD are not supported. Value passed: " + itos(year) + ".");
 	ERR_FAIL_COND_V_MSG(month > 12 || month == 0, 0, "Invalid month value of: " + itos(month) + ".");
 	ERR_FAIL_COND_V_MSG(month > 12 || month == 0, 0, "Invalid month value of: " + itos(month) + ".");
-
 	// Do this check after month is tested as valid
 	// Do this check after month is tested as valid
-	ERR_FAIL_COND_V_MSG(day > MONTH_DAYS_TABLE[LEAPYEAR(year)][month - 1] || day == 0, 0, "Invalid day value of '" + itos(day) + "' which is larger than '" + itos(MONTH_DAYS_TABLE[LEAPYEAR(year)][month - 1]) + "' or 0.");
+	unsigned int days_in_month = MONTH_DAYS_TABLE[LEAPYEAR(year)][month - 1];
+	ERR_FAIL_COND_V_MSG(day == 0 || day > days_in_month, 0, "Invalid day value of: " + itos(day) + ". It should be comprised between 1 and " + itos(days_in_month) + " for month " + itos(month) + ".");
+
 	// Calculate all the seconds from months past in this year
 	// Calculate all the seconds from months past in this year
 	uint64_t SECONDS_FROM_MONTHS_PAST_THIS_YEAR = DAYS_PAST_THIS_YEAR_TABLE[LEAPYEAR(year)][month - 1] * SECONDS_PER_DAY;
 	uint64_t SECONDS_FROM_MONTHS_PAST_THIS_YEAR = DAYS_PAST_THIS_YEAR_TABLE[LEAPYEAR(year)][month - 1] * SECONDS_PER_DAY;
 
 

+ 1 - 1
doc/classes/OS.xml

@@ -550,7 +550,7 @@
 			<description>
 			<description>
 				Gets an epoch time value from a dictionary of time values.
 				Gets an epoch time value from a dictionary of time values.
 				[code]datetime[/code] must be populated with the following keys: [code]year[/code], [code]month[/code], [code]day[/code], [code]hour[/code], [code]minute[/code], [code]second[/code].
 				[code]datetime[/code] must be populated with the following keys: [code]year[/code], [code]month[/code], [code]day[/code], [code]hour[/code], [code]minute[/code], [code]second[/code].
-				If the dictionary is empty [code]0[/code] is returned.
+				If the dictionary is empty [code]0[/code] is returned. If some keys are omitted, they default to the equivalent values for the UNIX epoch timestamp 0 (1970-01-01 at 00:00:00 UTC).
 				You can pass the output from [method get_datetime_from_unix_time] directly into this function. Daylight Savings Time ([code]dst[/code]), if present, is ignored.
 				You can pass the output from [method get_datetime_from_unix_time] directly into this function. Daylight Savings Time ([code]dst[/code]), if present, is ignored.
 			</description>
 			</description>
 		</method>
 		</method>

+ 1 - 0
doc/classes/Resource.xml

@@ -27,6 +27,7 @@
 			<description>
 			<description>
 				Duplicates the resource, returning a new resource. By default, sub-resources are shared between resource copies for efficiency. This can be changed by passing [code]true[/code] to the [code]subresources[/code] argument which will copy the subresources.
 				Duplicates the resource, returning a new resource. By default, sub-resources are shared between resource copies for efficiency. This can be changed by passing [code]true[/code] to the [code]subresources[/code] argument which will copy the subresources.
 				[b]Note:[/b] If [code]subresources[/code] is [code]true[/code], this method will only perform a shallow copy. Nested resources within subresources will not be duplicated and will still be shared.
 				[b]Note:[/b] If [code]subresources[/code] is [code]true[/code], this method will only perform a shallow copy. Nested resources within subresources will not be duplicated and will still be shared.
+				[b]Note:[/b] When duplicating a resource, only [code]export[/code]ed properties are copied. Other properties will be set to their default value in the new resource.
 			</description>
 			</description>
 		</method>
 		</method>
 		<method name="emit_changed">
 		<method name="emit_changed">

+ 6 - 0
doc/classes/TextureButton.xml

@@ -17,6 +17,12 @@
 		<member name="expand" type="bool" setter="set_expand" getter="get_expand" default="false">
 		<member name="expand" type="bool" setter="set_expand" getter="get_expand" default="false">
 			If [code]true[/code], the texture stretches to the edges of the node's bounding rectangle using the [member stretch_mode]. If [code]false[/code], the texture will not scale with the node.
 			If [code]true[/code], the texture stretches to the edges of the node's bounding rectangle using the [member stretch_mode]. If [code]false[/code], the texture will not scale with the node.
 		</member>
 		</member>
+		<member name="flip_h" type="bool" setter="set_flip_h" getter="is_flipped_h" default="false">
+			If [code]true[/code], texture is flipped horizontally.
+		</member>
+		<member name="flip_v" type="bool" setter="set_flip_v" getter="is_flipped_v" default="false">
+			If [code]true[/code], texture is flipped vertically.
+		</member>
 		<member name="stretch_mode" type="int" setter="set_stretch_mode" getter="get_stretch_mode" enum="TextureButton.StretchMode" default="0">
 		<member name="stretch_mode" type="int" setter="set_stretch_mode" getter="get_stretch_mode" enum="TextureButton.StretchMode" default="0">
 			Controls the texture's behavior when you resize the node's bounding rectangle, [b]only if[/b] [member expand] is [code]true[/code]. Set it to one of the [enum StretchMode] constants. See the constants to learn more.
 			Controls the texture's behavior when you resize the node's bounding rectangle, [b]only if[/b] [member expand] is [code]true[/code]. Set it to one of the [enum StretchMode] constants. See the constants to learn more.
 		</member>
 		</member>

+ 1 - 1
drivers/unix/dir_access_unix.cpp

@@ -432,7 +432,7 @@ uint64_t DirAccessUnix::get_space_left() {
 		return 0;
 		return 0;
 	};
 	};
 
 
-	return vfs.f_bfree * vfs.f_bsize;
+	return (uint64_t)vfs.f_bavail * (uint64_t)vfs.f_frsize;
 #else
 #else
 	// FIXME: Implement this.
 	// FIXME: Implement this.
 	return 0;
 	return 0;

+ 27 - 20
editor/editor_audio_buses.cpp

@@ -173,6 +173,9 @@ void EditorAudioBus::_notification(int p_what) {
 			bypass->set_icon(get_icon("AudioBusBypass", "EditorIcons"));
 			bypass->set_icon(get_icon("AudioBusBypass", "EditorIcons"));
 
 
 			bus_options->set_icon(get_icon("GuiTabMenuHl", "EditorIcons"));
 			bus_options->set_icon(get_icon("GuiTabMenuHl", "EditorIcons"));
+
+			audio_value_preview_box->add_color_override("font_color", get_color("font_color", "TooltipLabel"));
+			audio_value_preview_box->add_style_override("panel", get_stylebox("panel", "TooltipPanel"));
 		} break;
 		} break;
 		case NOTIFICATION_MOUSE_EXIT:
 		case NOTIFICATION_MOUSE_EXIT:
 		case NOTIFICATION_DRAG_END: {
 		case NOTIFICATION_DRAG_END: {
@@ -379,15 +382,24 @@ void EditorAudioBus::_show_value(float slider_value) {
 		db = _normalized_volume_to_scaled_db(slider_value);
 		db = _normalized_volume_to_scaled_db(slider_value);
 	}
 	}
 
 
-	String text = vformat("%10.1f dB", db);
+	String text;
+	if (Math::is_zero_approx(Math::stepify(db, 0.1))) {
+		// Prevent displaying `-0.0 dB` and show ` 0.0 dB` instead.
+		// The leading space makes the text visually line up with its positive/negative counterparts.
+		text = " 0.0 dB";
+	} else {
+		// Show an explicit `+` sign if positive.
+		text = vformat("%+.1f dB", db);
+	}
 
 
+	// Also set the preview text as a standard Control tooltip.
+	// This way, it can be seen when the slider is merely hovered (instead of dragged).
 	slider->set_tooltip(text);
 	slider->set_tooltip(text);
 	audio_value_preview_label->set_text(text);
 	audio_value_preview_label->set_text(text);
-	Vector2 slider_size = slider->get_size();
-	Vector2 slider_position = slider->get_global_position();
-	float left_padding = 5.0f;
-	float vert_padding = 10.0f;
-	Vector2 box_position = Vector2(slider_size.x + left_padding, (slider_size.y - vert_padding) * (1.0f - slider->get_value()) - vert_padding);
+	const Vector2 slider_size = slider->get_size();
+	const Vector2 slider_position = slider->get_global_position();
+	const float vert_padding = 10.0f;
+	const Vector2 box_position = Vector2(slider_size.x, (slider_size.y - vert_padding) * (1.0f - slider->get_value()) - vert_padding);
 	audio_value_preview_box->set_position(slider_position + box_position);
 	audio_value_preview_box->set_position(slider_position + box_position);
 	audio_value_preview_box->set_size(audio_value_preview_label->get_size());
 	audio_value_preview_box->set_size(audio_value_preview_label->get_size());
 	if (slider->has_focus() && !audio_value_preview_box->is_visible()) {
 	if (slider->has_focus() && !audio_value_preview_box->is_visible()) {
@@ -818,7 +830,7 @@ EditorAudioBus::EditorAudioBus(EditorAudioBuses *p_buses, bool p_is_master) {
 	bus_options = memnew(MenuButton);
 	bus_options = memnew(MenuButton);
 	bus_options->set_h_size_flags(SIZE_SHRINK_END);
 	bus_options->set_h_size_flags(SIZE_SHRINK_END);
 	bus_options->set_anchor(MARGIN_RIGHT, 0.0);
 	bus_options->set_anchor(MARGIN_RIGHT, 0.0);
-	bus_options->set_tooltip(TTR("Bus options"));
+	bus_options->set_tooltip(TTR("Bus Options"));
 	hbc->add_child(bus_options);
 	hbc->add_child(bus_options);
 
 
 	Ref<StyleBoxEmpty> sbempty = memnew(StyleBoxEmpty);
 	Ref<StyleBoxEmpty> sbempty = memnew(StyleBoxEmpty);
@@ -853,14 +865,13 @@ EditorAudioBus::EditorAudioBus(EditorAudioBuses *p_buses, bool p_is_master) {
 	audio_value_preview_label->set_v_size_flags(SIZE_EXPAND_FILL);
 	audio_value_preview_label->set_v_size_flags(SIZE_EXPAND_FILL);
 	audio_value_preview_label->set_h_size_flags(SIZE_EXPAND_FILL);
 	audio_value_preview_label->set_h_size_flags(SIZE_EXPAND_FILL);
 	audio_value_preview_label->set_mouse_filter(MOUSE_FILTER_PASS);
 	audio_value_preview_label->set_mouse_filter(MOUSE_FILTER_PASS);
+	audio_value_preview_box->add_color_override("font_color", get_color("font_color", "TooltipLabel"));
 
 
 	audioprev_hbc->add_child(audio_value_preview_label);
 	audioprev_hbc->add_child(audio_value_preview_label);
 
 
 	slider->add_child(audio_value_preview_box);
 	slider->add_child(audio_value_preview_box);
 	audio_value_preview_box->set_as_toplevel(true);
 	audio_value_preview_box->set_as_toplevel(true);
-	Ref<StyleBoxFlat> panel_style = memnew(StyleBoxFlat);
-	panel_style->set_bg_color(Color(0.0f, 0.0f, 0.0f, 0.8f));
-	audio_value_preview_box->add_style_override("panel", panel_style);
+	audio_value_preview_box->add_style_override("panel", get_stylebox("panel", "TooltipPanel"));
 	audio_value_preview_box->set_mouse_filter(MOUSE_FILTER_PASS);
 	audio_value_preview_box->set_mouse_filter(MOUSE_FILTER_PASS);
 	audio_value_preview_box->hide();
 	audio_value_preview_box->hide();
 
 
@@ -1427,7 +1438,7 @@ void EditorAudioMeterNotches::_bind_methods() {
 void EditorAudioMeterNotches::_notification(int p_what) {
 void EditorAudioMeterNotches::_notification(int p_what) {
 	switch (p_what) {
 	switch (p_what) {
 		case NOTIFICATION_THEME_CHANGED: {
 		case NOTIFICATION_THEME_CHANGED: {
-			notch_color = EditorSettings::get_singleton()->is_dark_theme() ? Color(1, 1, 1) : Color(0, 0, 0);
+			notch_color = get_color("font_color", "Editor");
 		} break;
 		} break;
 		case NOTIFICATION_DRAW: {
 		case NOTIFICATION_DRAW: {
 			_draw_audio_notches();
 			_draw_audio_notches();
@@ -1442,13 +1453,13 @@ void EditorAudioMeterNotches::_draw_audio_notches() {
 	for (int i = 0; i < notches.size(); i++) {
 	for (int i = 0; i < notches.size(); i++) {
 		AudioNotch n = notches[i];
 		AudioNotch n = notches[i];
 		draw_line(Vector2(0, (1.0f - n.relative_position) * (get_size().y - btm_padding - top_padding) + top_padding),
 		draw_line(Vector2(0, (1.0f - n.relative_position) * (get_size().y - btm_padding - top_padding) + top_padding),
-				Vector2(line_length, (1.0f - n.relative_position) * (get_size().y - btm_padding - top_padding) + top_padding),
+				Vector2(line_length * EDSCALE, (1.0f - n.relative_position) * (get_size().y - btm_padding - top_padding) + top_padding),
 				notch_color,
 				notch_color,
-				1);
+				Math::round(EDSCALE));
 
 
 		if (n.render_db_value) {
 		if (n.render_db_value) {
 			draw_string(font,
 			draw_string(font,
-					Vector2(line_length + label_space,
+					Vector2((line_length + label_space) * EDSCALE,
 							(1.0f - n.relative_position) * (get_size().y - btm_padding - top_padding) + (font_height / 4) + top_padding),
 							(1.0f - n.relative_position) * (get_size().y - btm_padding - top_padding) + (font_height / 4) + top_padding),
 					String::num(Math::abs(n.db_value)) + "dB",
 					String::num(Math::abs(n.db_value)) + "dB",
 					notch_color);
 					notch_color);
@@ -1456,10 +1467,6 @@ void EditorAudioMeterNotches::_draw_audio_notches() {
 	}
 	}
 }
 }
 
 
-EditorAudioMeterNotches::EditorAudioMeterNotches() :
-		line_length(5.0f),
-		label_space(2.0f),
-		btm_padding(9.0f),
-		top_padding(5.0f) {
-	notch_color = EditorSettings::get_singleton()->is_dark_theme() ? Color(1, 1, 1) : Color(0, 0, 0);
+EditorAudioMeterNotches::EditorAudioMeterNotches() {
+	notch_color = get_color("font_color", "Editor");
 }
 }

+ 4 - 4
editor/editor_audio_buses.h

@@ -244,10 +244,10 @@ private:
 	List<AudioNotch> notches;
 	List<AudioNotch> notches;
 
 
 public:
 public:
-	float line_length;
-	float label_space;
-	float btm_padding;
-	float top_padding;
+	const float line_length = 5.0f;
+	const float label_space = 2.0f;
+	const float btm_padding = 9.0f;
+	const float top_padding = 5.0f;
 	Color notch_color;
 	Color notch_color;
 
 
 	void add_notch(float p_normalized_offset, float p_db_value, bool p_render_value = false);
 	void add_notch(float p_normalized_offset, float p_db_value, bool p_render_value = false);

+ 37 - 0
editor/editor_feature_profile.cpp

@@ -116,6 +116,18 @@ bool EditorFeatureProfile::has_class_properties_disabled(const StringName &p_cla
 	return disabled_properties.has(p_class);
 	return disabled_properties.has(p_class);
 }
 }
 
 
+void EditorFeatureProfile::set_item_collapsed(const StringName &p_class, bool p_collapsed) {
+	if (p_collapsed) {
+		collapsed_classes.insert(p_class);
+	} else {
+		collapsed_classes.erase(p_class);
+	}
+}
+
+bool EditorFeatureProfile::is_item_collapsed(const StringName &p_class) const {
+	return collapsed_classes.has(p_class);
+}
+
 void EditorFeatureProfile::set_disable_feature(Feature p_feature, bool p_disable) {
 void EditorFeatureProfile::set_disable_feature(Feature p_feature, bool p_disable) {
 	ERR_FAIL_INDEX(p_feature, FEATURE_MAX);
 	ERR_FAIL_INDEX(p_feature, FEATURE_MAX);
 	features_disabled[p_feature] = p_disable;
 	features_disabled[p_feature] = p_disable;
@@ -478,6 +490,9 @@ void EditorFeatureProfileManager::_fill_classes_from(TreeItem *p_parent, const S
 	class_item->set_selectable(0, true);
 	class_item->set_selectable(0, true);
 	class_item->set_metadata(0, p_class);
 	class_item->set_metadata(0, p_class);
 
 
+	bool collapsed = edited->is_item_collapsed(p_class);
+	class_item->set_collapsed(collapsed);
+
 	if (p_class == p_selected) {
 	if (p_class == p_selected) {
 		class_item->select(0);
 		class_item->select(0);
 	}
 	}
@@ -590,6 +605,26 @@ void EditorFeatureProfileManager::_class_list_item_edited() {
 	}
 	}
 }
 }
 
 
+void EditorFeatureProfileManager::_class_list_item_collapsed(Object *p_item) {
+	if (updating_features) {
+		return;
+	}
+
+	TreeItem *item = Object::cast_to<TreeItem>(p_item);
+	if (!item) {
+		return;
+	}
+
+	Variant md = item->get_metadata(0);
+	if (md.get_type() != Variant::STRING) {
+		return;
+	}
+
+	String class_name = md;
+	bool collapsed = item->is_collapsed();
+	edited->set_item_collapsed(class_name, collapsed);
+}
+
 void EditorFeatureProfileManager::_property_item_edited() {
 void EditorFeatureProfileManager::_property_item_edited() {
 	if (updating_features) {
 	if (updating_features) {
 		return;
 		return;
@@ -781,6 +816,7 @@ void EditorFeatureProfileManager::_bind_methods() {
 	ClassDB::bind_method("_export_profile", &EditorFeatureProfileManager::_export_profile);
 	ClassDB::bind_method("_export_profile", &EditorFeatureProfileManager::_export_profile);
 	ClassDB::bind_method("_class_list_item_selected", &EditorFeatureProfileManager::_class_list_item_selected);
 	ClassDB::bind_method("_class_list_item_selected", &EditorFeatureProfileManager::_class_list_item_selected);
 	ClassDB::bind_method("_class_list_item_edited", &EditorFeatureProfileManager::_class_list_item_edited);
 	ClassDB::bind_method("_class_list_item_edited", &EditorFeatureProfileManager::_class_list_item_edited);
+	ClassDB::bind_method("_class_list_item_collapsed", &EditorFeatureProfileManager::_class_list_item_collapsed);
 	ClassDB::bind_method("_property_item_edited", &EditorFeatureProfileManager::_property_item_edited);
 	ClassDB::bind_method("_property_item_edited", &EditorFeatureProfileManager::_property_item_edited);
 	ClassDB::bind_method("_emit_current_profile_changed", &EditorFeatureProfileManager::_emit_current_profile_changed);
 	ClassDB::bind_method("_emit_current_profile_changed", &EditorFeatureProfileManager::_emit_current_profile_changed);
 
 
@@ -852,6 +888,7 @@ EditorFeatureProfileManager::EditorFeatureProfileManager() {
 	class_list->set_edit_checkbox_cell_only_when_checkbox_is_pressed(true);
 	class_list->set_edit_checkbox_cell_only_when_checkbox_is_pressed(true);
 	class_list->connect("cell_selected", this, "_class_list_item_selected");
 	class_list->connect("cell_selected", this, "_class_list_item_selected");
 	class_list->connect("item_edited", this, "_class_list_item_edited", varray(), CONNECT_DEFERRED);
 	class_list->connect("item_edited", this, "_class_list_item_edited", varray(), CONNECT_DEFERRED);
+	class_list->connect("item_collapsed", this, "_class_list_item_collapsed");
 
 
 	VBoxContainer *property_list_vbc = memnew(VBoxContainer);
 	VBoxContainer *property_list_vbc = memnew(VBoxContainer);
 	h_split->add_child(property_list_vbc);
 	h_split->add_child(property_list_vbc);

+ 6 - 0
editor/editor_feature_profile.h

@@ -60,6 +60,8 @@ private:
 	Set<StringName> disabled_editors;
 	Set<StringName> disabled_editors;
 	Map<StringName, Set<StringName>> disabled_properties;
 	Map<StringName, Set<StringName>> disabled_properties;
 
 
+	Set<StringName> collapsed_classes;
+
 	bool features_disabled[FEATURE_MAX];
 	bool features_disabled[FEATURE_MAX];
 	static const char *feature_names[FEATURE_MAX];
 	static const char *feature_names[FEATURE_MAX];
 	static const char *feature_identifiers[FEATURE_MAX];
 	static const char *feature_identifiers[FEATURE_MAX];
@@ -80,6 +82,9 @@ public:
 	bool is_class_property_disabled(const StringName &p_class, const StringName &p_property) const;
 	bool is_class_property_disabled(const StringName &p_class, const StringName &p_property) const;
 	bool has_class_properties_disabled(const StringName &p_class) const;
 	bool has_class_properties_disabled(const StringName &p_class) const;
 
 
+	void set_item_collapsed(const StringName &p_class, bool p_collapsed);
+	bool is_item_collapsed(const StringName &p_class) const;
+
 	void set_disable_feature(Feature p_feature, bool p_disable);
 	void set_disable_feature(Feature p_feature, bool p_disable);
 	bool is_feature_disabled(Feature p_feature) const;
 	bool is_feature_disabled(Feature p_feature) const;
 
 
@@ -148,6 +153,7 @@ class EditorFeatureProfileManager : public AcceptDialog {
 
 
 	void _class_list_item_selected();
 	void _class_list_item_selected();
 	void _class_list_item_edited();
 	void _class_list_item_edited();
+	void _class_list_item_collapsed(Object *p_item);
 	void _property_item_edited();
 	void _property_item_edited();
 	void _save_and_update();
 	void _save_and_update();
 
 

+ 1 - 0
editor/plugin_config_dialog.cpp

@@ -209,6 +209,7 @@ PluginConfigDialog::PluginConfigDialog() {
 
 
 	desc_edit = memnew(TextEdit);
 	desc_edit = memnew(TextEdit);
 	desc_edit->set_custom_minimum_size(Size2(400, 80) * EDSCALE);
 	desc_edit->set_custom_minimum_size(Size2(400, 80) * EDSCALE);
+	desc_edit->set_wrap_enabled(true);
 	grid->add_child(desc_edit);
 	grid->add_child(desc_edit);
 
 
 	Label *author_lb = memnew(Label);
 	Label *author_lb = memnew(Label);

+ 2 - 0
editor/plugins/canvas_item_editor_plugin.cpp

@@ -5252,8 +5252,10 @@ void CanvasItemEditor::_bind_methods() {
 	ClassDB::bind_method("_popup_warning_depop", &CanvasItemEditor::_popup_warning_depop);
 	ClassDB::bind_method("_popup_warning_depop", &CanvasItemEditor::_popup_warning_depop);
 	ClassDB::bind_method(D_METHOD("_selection_result_pressed"), &CanvasItemEditor::_selection_result_pressed);
 	ClassDB::bind_method(D_METHOD("_selection_result_pressed"), &CanvasItemEditor::_selection_result_pressed);
 	ClassDB::bind_method(D_METHOD("_selection_menu_hide"), &CanvasItemEditor::_selection_menu_hide);
 	ClassDB::bind_method(D_METHOD("_selection_menu_hide"), &CanvasItemEditor::_selection_menu_hide);
+	ClassDB::bind_method(D_METHOD("get_state"), &CanvasItemEditor::get_state);
 	ClassDB::bind_method(D_METHOD("set_state"), &CanvasItemEditor::set_state);
 	ClassDB::bind_method(D_METHOD("set_state"), &CanvasItemEditor::set_state);
 	ClassDB::bind_method(D_METHOD("update_viewport"), &CanvasItemEditor::update_viewport);
 	ClassDB::bind_method(D_METHOD("update_viewport"), &CanvasItemEditor::update_viewport);
+	ClassDB::bind_method(D_METHOD("_zoom_on_position"), &CanvasItemEditor::_zoom_on_position);
 
 
 	ADD_SIGNAL(MethodInfo("item_lock_status_changed"));
 	ADD_SIGNAL(MethodInfo("item_lock_status_changed"));
 	ADD_SIGNAL(MethodInfo("item_group_status_changed"));
 	ADD_SIGNAL(MethodInfo("item_group_status_changed"));

+ 9 - 2
editor/plugins/script_text_editor.cpp

@@ -31,6 +31,7 @@
 #include "script_text_editor.h"
 #include "script_text_editor.h"
 
 
 #include "core/math/expression.h"
 #include "core/math/expression.h"
+#include "core/os/input.h"
 #include "core/os/keyboard.h"
 #include "core/os/keyboard.h"
 #include "editor/editor_node.h"
 #include "editor/editor_node.h"
 #include "editor/editor_scale.h"
 #include "editor/editor_scale.h"
@@ -1506,11 +1507,17 @@ void ScriptTextEditor::drop_data_fw(const Point2 &p_point, const Variant &p_data
 		Array files = d["files"];
 		Array files = d["files"];
 
 
 		String text_to_drop;
 		String text_to_drop;
+		bool preload = Input::get_singleton()->is_key_pressed(KEY_CONTROL);
 		for (int i = 0; i < files.size(); i++) {
 		for (int i = 0; i < files.size(); i++) {
 			if (i > 0) {
 			if (i > 0) {
-				text_to_drop += ",";
+				text_to_drop += ", ";
+			}
+
+			if (preload) {
+				text_to_drop += "preload(\"" + String(files[i]).c_escape() + "\")";
+			} else {
+				text_to_drop += "\"" + String(files[i]).c_escape() + "\"";
 			}
 			}
-			text_to_drop += "\"" + String(files[i]).c_escape() + "\"";
 		}
 		}
 
 
 		te->cursor_set_line(row);
 		te->cursor_set_line(row);

+ 4 - 4
misc/dist/ios_xcode/godot_ios.xcodeproj/project.pbxproj

@@ -247,7 +247,7 @@
 				IPHONEOS_DEPLOYMENT_TARGET = 10.0;
 				IPHONEOS_DEPLOYMENT_TARGET = 10.0;
 				OTHER_LDFLAGS = "$linker_flags";
 				OTHER_LDFLAGS = "$linker_flags";
 				SDKROOT = iphoneos;
 				SDKROOT = iphoneos;
-				TARGETED_DEVICE_FAMILY = "1,2";
+				TARGETED_DEVICE_FAMILY = "$targeted_device_family";
 			};
 			};
 			name = Debug;
 			name = Debug;
 		};
 		};
@@ -286,7 +286,7 @@
 				IPHONEOS_DEPLOYMENT_TARGET = 10.0;
 				IPHONEOS_DEPLOYMENT_TARGET = 10.0;
 				OTHER_LDFLAGS = "$linker_flags";
 				OTHER_LDFLAGS = "$linker_flags";
 				SDKROOT = iphoneos;
 				SDKROOT = iphoneos;
-				TARGETED_DEVICE_FAMILY = "1,2";
+				TARGETED_DEVICE_FAMILY = "$targeted_device_family";
 				VALIDATE_PRODUCT = YES;
 				VALIDATE_PRODUCT = YES;
 			};
 			};
 			name = Release;
 			name = Release;
@@ -315,7 +315,7 @@
 				PRODUCT_BUNDLE_IDENTIFIER = $identifier;
 				PRODUCT_BUNDLE_IDENTIFIER = $identifier;
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				PROVISIONING_PROFILE = "$provisioning_profile_uuid_debug";
 				PROVISIONING_PROFILE = "$provisioning_profile_uuid_debug";
-				TARGETED_DEVICE_FAMILY = "1,2";
+				TARGETED_DEVICE_FAMILY = "$targeted_device_family";
 				VALID_ARCHS = "armv7 armv7s arm64 i386 x86_64";
 				VALID_ARCHS = "armv7 armv7s arm64 i386 x86_64";
 				WRAPPER_EXTENSION = app;
 				WRAPPER_EXTENSION = app;
 			};
 			};
@@ -345,7 +345,7 @@
 				PRODUCT_BUNDLE_IDENTIFIER = $identifier;
 				PRODUCT_BUNDLE_IDENTIFIER = $identifier;
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				PROVISIONING_PROFILE = "$provisioning_profile_uuid_release";
 				PROVISIONING_PROFILE = "$provisioning_profile_uuid_release";
-				TARGETED_DEVICE_FAMILY = "1,2";
+				TARGETED_DEVICE_FAMILY = "$targeted_device_family";
 				VALID_ARCHS = "armv7 armv7s arm64 i386 x86_64";
 				VALID_ARCHS = "armv7 armv7s arm64 i386 x86_64";
 				WRAPPER_EXTENSION = app;
 				WRAPPER_EXTENSION = app;
 			};
 			};

+ 5 - 5
misc/dist/ios_xcode/godot_ios.xcodeproj/xcshareddata/xcschemes/godot_ios.xcscheme

@@ -23,7 +23,7 @@
       </BuildActionEntries>
       </BuildActionEntries>
    </BuildAction>
    </BuildAction>
    <TestAction
    <TestAction
-      buildConfiguration = "Debug"
+      buildConfiguration = "$default_build_config"
       selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
       selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
       selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
       selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
       shouldUseLaunchSchemeArgsEnv = "YES">
       shouldUseLaunchSchemeArgsEnv = "YES">
@@ -42,7 +42,7 @@
       </AdditionalOptions>
       </AdditionalOptions>
    </TestAction>
    </TestAction>
    <LaunchAction
    <LaunchAction
-      buildConfiguration = "Debug"
+      buildConfiguration = "$default_build_config"
       selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
       selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
       selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
       selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
       launchStyle = "0"
       launchStyle = "0"
@@ -67,7 +67,7 @@
       </AdditionalOptions>
       </AdditionalOptions>
    </LaunchAction>
    </LaunchAction>
    <ProfileAction
    <ProfileAction
-      buildConfiguration = "Debug"
+      buildConfiguration = "$default_build_config"
       shouldUseLaunchSchemeArgsEnv = "YES"
       shouldUseLaunchSchemeArgsEnv = "YES"
       savedToolIdentifier = ""
       savedToolIdentifier = ""
       useCustomWorkingDirectory = "NO"
       useCustomWorkingDirectory = "NO"
@@ -84,10 +84,10 @@
       </BuildableProductRunnable>
       </BuildableProductRunnable>
    </ProfileAction>
    </ProfileAction>
    <AnalyzeAction
    <AnalyzeAction
-      buildConfiguration = "Debug">
+      buildConfiguration = "$default_build_config">
    </AnalyzeAction>
    </AnalyzeAction>
    <ArchiveAction
    <ArchiveAction
-      buildConfiguration = "Debug"
+      buildConfiguration = "$default_build_config"
       revealArchiveInOrganizer = "YES">
       revealArchiveInOrganizer = "YES">
    </ArchiveAction>
    </ArchiveAction>
 </Scheme>
 </Scheme>

+ 2 - 0
modules/gdnative/pluginscript/pluginscript_script.cpp

@@ -203,6 +203,8 @@ ScriptInstance *PluginScript::instance_create(Object *p_this) {
 }
 }
 
 
 bool PluginScript::instance_has(const Object *p_this) const {
 bool PluginScript::instance_has(const Object *p_this) const {
+	ERR_FAIL_COND_V(!_language, false);
+
 	_language->lock();
 	_language->lock();
 	bool hasit = _instances.has((Object *)p_this);
 	bool hasit = _instances.has((Object *)p_this);
 	_language->unlock();
 	_language->unlock();

+ 3 - 0
modules/opensimplex/doc_classes/NoiseTexture.xml

@@ -31,6 +31,9 @@
 		<member name="noise" type="OpenSimplexNoise" setter="set_noise" getter="get_noise">
 		<member name="noise" type="OpenSimplexNoise" setter="set_noise" getter="get_noise">
 			The [OpenSimplexNoise] instance used to generate the noise.
 			The [OpenSimplexNoise] instance used to generate the noise.
 		</member>
 		</member>
+		<member name="noise_offset" type="Vector2" setter="set_noise_offset" getter="get_noise_offset" default="Vector2( 0, 0 )">
+			An offset used to specify the noise space coordinate of the top left corner of the generated noise. This value is ignored if [member seamless] is enabled.
+		</member>
 		<member name="seamless" type="bool" setter="set_seamless" getter="get_seamless" default="false">
 		<member name="seamless" type="bool" setter="set_seamless" getter="get_seamless" default="false">
 			Whether the texture can be tiled without visible seams or not. Seamless textures take longer to generate.
 			Whether the texture can be tiled without visible seams or not. Seamless textures take longer to generate.
 			[b]Note:[/b] Seamless noise has a lower contrast compared to non-seamless noise. This is due to the way noise uses higher dimensions for generating seamless noise.
 			[b]Note:[/b] Seamless noise has a lower contrast compared to non-seamless noise. This is due to the way noise uses higher dimensions for generating seamless noise.

+ 3 - 1
modules/opensimplex/doc_classes/OpenSimplexNoise.xml

@@ -31,8 +31,10 @@
 			</argument>
 			</argument>
 			<argument index="1" name="height" type="int">
 			<argument index="1" name="height" type="int">
 			</argument>
 			</argument>
+			<argument index="2" name="noise_offset" type="Vector2" default="Vector2( 0, 0 )">
+			</argument>
 			<description>
 			<description>
-				Generate a noise image in [constant Image.FORMAT_L8] format with the requested [code]width[/code] and [code]height[/code], based on the current noise parameters.
+				Generate a noise image in [constant Image.FORMAT_L8] format with the requested [code]width[/code] and [code]height[/code], based on the current noise parameters. If [code]noise_offset[/code] is specified, then the offset value is used as the coordinates of the top-left corner of the generated noise.
 			</description>
 			</description>
 		</method>
 		</method>
 		<method name="get_noise_1d" qualifiers="const">
 		<method name="get_noise_1d" qualifiers="const">

+ 17 - 1
modules/opensimplex/noise_texture.cpp

@@ -62,6 +62,9 @@ void NoiseTexture::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("set_noise", "noise"), &NoiseTexture::set_noise);
 	ClassDB::bind_method(D_METHOD("set_noise", "noise"), &NoiseTexture::set_noise);
 	ClassDB::bind_method(D_METHOD("get_noise"), &NoiseTexture::get_noise);
 	ClassDB::bind_method(D_METHOD("get_noise"), &NoiseTexture::get_noise);
 
 
+	ClassDB::bind_method(D_METHOD("set_noise_offset", "noise_offset"), &NoiseTexture::set_noise_offset);
+	ClassDB::bind_method(D_METHOD("get_noise_offset"), &NoiseTexture::get_noise_offset);
+
 	ClassDB::bind_method(D_METHOD("set_seamless", "seamless"), &NoiseTexture::set_seamless);
 	ClassDB::bind_method(D_METHOD("set_seamless", "seamless"), &NoiseTexture::set_seamless);
 	ClassDB::bind_method(D_METHOD("get_seamless"), &NoiseTexture::get_seamless);
 	ClassDB::bind_method(D_METHOD("get_seamless"), &NoiseTexture::get_seamless);
 
 
@@ -82,6 +85,7 @@ void NoiseTexture::_bind_methods() {
 	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "as_normalmap"), "set_as_normalmap", "is_normalmap");
 	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "as_normalmap"), "set_as_normalmap", "is_normalmap");
 	ADD_PROPERTY(PropertyInfo(Variant::REAL, "bump_strength", PROPERTY_HINT_RANGE, "0,32,0.1,or_greater"), "set_bump_strength", "get_bump_strength");
 	ADD_PROPERTY(PropertyInfo(Variant::REAL, "bump_strength", PROPERTY_HINT_RANGE, "0,32,0.1,or_greater"), "set_bump_strength", "get_bump_strength");
 	ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "noise", PROPERTY_HINT_RESOURCE_TYPE, "OpenSimplexNoise"), "set_noise", "get_noise");
 	ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "noise", PROPERTY_HINT_RESOURCE_TYPE, "OpenSimplexNoise"), "set_noise", "get_noise");
+	ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "noise_offset"), "set_noise_offset", "get_noise_offset");
 }
 }
 
 
 void NoiseTexture::_validate_property(PropertyInfo &property) const {
 void NoiseTexture::_validate_property(PropertyInfo &property) const {
@@ -137,7 +141,7 @@ Ref<Image> NoiseTexture::_generate_texture() {
 	if (seamless) {
 	if (seamless) {
 		image = ref_noise->get_seamless_image(size.x);
 		image = ref_noise->get_seamless_image(size.x);
 	} else {
 	} else {
-		image = ref_noise->get_image(size.x, size.y);
+		image = ref_noise->get_image(size.x, size.y, noise_offset);
 	}
 	}
 
 
 	if (as_normalmap) {
 	if (as_normalmap) {
@@ -205,6 +209,14 @@ void NoiseTexture::set_height(int p_height) {
 	_queue_update();
 	_queue_update();
 }
 }
 
 
+void NoiseTexture::set_noise_offset(Vector2 p_noise_offset) {
+	if (noise_offset == p_noise_offset) {
+		return;
+	}
+	noise_offset = p_noise_offset;
+	_queue_update();
+}
+
 void NoiseTexture::set_seamless(bool p_seamless) {
 void NoiseTexture::set_seamless(bool p_seamless) {
 	if (p_seamless == seamless) {
 	if (p_seamless == seamless) {
 		return;
 		return;
@@ -252,6 +264,10 @@ int NoiseTexture::get_height() const {
 	return size.y;
 	return size.y;
 }
 }
 
 
+Vector2 NoiseTexture::get_noise_offset() const {
+	return noise_offset;
+}
+
 void NoiseTexture::set_flags(uint32_t p_flags) {
 void NoiseTexture::set_flags(uint32_t p_flags) {
 	flags = p_flags;
 	flags = p_flags;
 	VS::get_singleton()->texture_set_flags(texture, flags);
 	VS::get_singleton()->texture_set_flags(texture, flags);

+ 4 - 0
modules/opensimplex/noise_texture.h

@@ -56,6 +56,7 @@ private:
 
 
 	Ref<OpenSimplexNoise> noise;
 	Ref<OpenSimplexNoise> noise;
 	Vector2i size;
 	Vector2i size;
+	Vector2 noise_offset;
 	bool seamless;
 	bool seamless;
 	bool as_normalmap;
 	bool as_normalmap;
 	float bump_strength;
 	float bump_strength;
@@ -79,6 +80,9 @@ public:
 	void set_width(int p_width);
 	void set_width(int p_width);
 	void set_height(int p_height);
 	void set_height(int p_height);
 
 
+	void set_noise_offset(Vector2 p_noise_offset);
+	Vector2 get_noise_offset() const;
+
 	void set_seamless(bool p_seamless);
 	void set_seamless(bool p_seamless);
 	bool get_seamless();
 	bool get_seamless();
 
 

+ 3 - 3
modules/opensimplex/open_simplex_noise.cpp

@@ -102,7 +102,7 @@ void OpenSimplexNoise::set_lacunarity(float p_lacunarity) {
 	emit_changed();
 	emit_changed();
 }
 }
 
 
-Ref<Image> OpenSimplexNoise::get_image(int p_width, int p_height) const {
+Ref<Image> OpenSimplexNoise::get_image(int p_width, int p_height, const Vector2 &p_noise_offset) const {
 	PoolVector<uint8_t> data;
 	PoolVector<uint8_t> data;
 	data.resize(p_width * p_height);
 	data.resize(p_width * p_height);
 
 
@@ -110,7 +110,7 @@ Ref<Image> OpenSimplexNoise::get_image(int p_width, int p_height) const {
 
 
 	for (int i = 0; i < p_height; i++) {
 	for (int i = 0; i < p_height; i++) {
 		for (int j = 0; j < p_width; j++) {
 		for (int j = 0; j < p_width; j++) {
-			float v = get_noise_2d(i, j);
+			float v = get_noise_2d(float(j) + p_noise_offset.x, float(i) + p_noise_offset.y);
 			v = v * 0.5 + 0.5; // Normalize [0..1]
 			v = v * 0.5 + 0.5; // Normalize [0..1]
 			wd8[(i * p_width + j)] = uint8_t(CLAMP(v * 255.0, 0, 255));
 			wd8[(i * p_width + j)] = uint8_t(CLAMP(v * 255.0, 0, 255));
 		}
 		}
@@ -167,7 +167,7 @@ void OpenSimplexNoise::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("set_lacunarity", "lacunarity"), &OpenSimplexNoise::set_lacunarity);
 	ClassDB::bind_method(D_METHOD("set_lacunarity", "lacunarity"), &OpenSimplexNoise::set_lacunarity);
 	ClassDB::bind_method(D_METHOD("get_lacunarity"), &OpenSimplexNoise::get_lacunarity);
 	ClassDB::bind_method(D_METHOD("get_lacunarity"), &OpenSimplexNoise::get_lacunarity);
 
 
-	ClassDB::bind_method(D_METHOD("get_image", "width", "height"), &OpenSimplexNoise::get_image);
+	ClassDB::bind_method(D_METHOD("get_image", "width", "height", "noise_offset"), &OpenSimplexNoise::get_image, DEFVAL(Vector2()));
 	ClassDB::bind_method(D_METHOD("get_seamless_image", "size"), &OpenSimplexNoise::get_seamless_image);
 	ClassDB::bind_method(D_METHOD("get_seamless_image", "size"), &OpenSimplexNoise::get_seamless_image);
 
 
 	ClassDB::bind_method(D_METHOD("get_noise_1d", "x"), &OpenSimplexNoise::get_noise_1d);
 	ClassDB::bind_method(D_METHOD("get_noise_1d", "x"), &OpenSimplexNoise::get_noise_1d);

+ 1 - 1
modules/opensimplex/open_simplex_noise.h

@@ -75,7 +75,7 @@ public:
 	void set_lacunarity(float p_lacunarity);
 	void set_lacunarity(float p_lacunarity);
 	float get_lacunarity() const { return lacunarity; }
 	float get_lacunarity() const { return lacunarity; }
 
 
-	Ref<Image> get_image(int p_width, int p_height) const;
+	Ref<Image> get_image(int p_width, int p_height, const Vector2 &p_noise_offset = Vector2()) const;
 	Ref<Image> get_seamless_image(int p_size) const;
 	Ref<Image> get_seamless_image(int p_size) const;
 
 
 	float get_noise_1d(float x) const;
 	float get_noise_1d(float x) const;

+ 1 - 0
modules/visual_script/visual_script_builtin_funcs.cpp

@@ -134,6 +134,7 @@ bool VisualScriptBuiltinFunc::has_input_sequence_port() const {
 		case TEXT_PRINT:
 		case TEXT_PRINT:
 		case TEXT_PRINTERR:
 		case TEXT_PRINTERR:
 		case TEXT_PRINTRAW:
 		case TEXT_PRINTRAW:
+		case MATH_SEED:
 			return true;
 			return true;
 		default:
 		default:
 			return false;
 			return false;

+ 18 - 0
platform/iphone/export/export.cpp

@@ -352,6 +352,8 @@ void EditorExportPlatformIOS::get_export_options(List<ExportOption> *r_options)
 	r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/code_sign_identity_release", PROPERTY_HINT_PLACEHOLDER_TEXT, "iPhone Distribution"), ""));
 	r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/code_sign_identity_release", PROPERTY_HINT_PLACEHOLDER_TEXT, "iPhone Distribution"), ""));
 	r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "application/export_method_release", PROPERTY_HINT_ENUM, "App Store,Development,Ad-Hoc,Enterprise"), 0));
 	r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "application/export_method_release", PROPERTY_HINT_ENUM, "App Store,Development,Ad-Hoc,Enterprise"), 0));
 
 
+	r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "application/targeted_device_family", PROPERTY_HINT_ENUM, "iPhone,iPad,iPhone & iPad"), 2));
+
 	r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/name", PROPERTY_HINT_PLACEHOLDER_TEXT, "Game Name"), ""));
 	r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/name", PROPERTY_HINT_PLACEHOLDER_TEXT, "Game Name"), ""));
 	r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/info"), "Made with Godot Engine"));
 	r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/info"), "Made with Godot Engine"));
 	r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/identifier", PROPERTY_HINT_PLACEHOLDER_TEXT, "com.example.game"), ""));
 	r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/identifier", PROPERTY_HINT_PLACEHOLDER_TEXT, "com.example.game"), ""));
@@ -444,6 +446,8 @@ void EditorExportPlatformIOS::_fix_config_file(const Ref<EditorExportPreset> &p_
 			strnew += lines[i].replace("$copyright", p_preset->get("application/copyright")) + "\n";
 			strnew += lines[i].replace("$copyright", p_preset->get("application/copyright")) + "\n";
 		} else if (lines[i].find("$team_id") != -1) {
 		} else if (lines[i].find("$team_id") != -1) {
 			strnew += lines[i].replace("$team_id", p_preset->get("application/app_store_team_id")) + "\n";
 			strnew += lines[i].replace("$team_id", p_preset->get("application/app_store_team_id")) + "\n";
+		} else if (lines[i].find("$default_build_config") != -1) {
+			strnew += lines[i].replace("$default_build_config", p_debug ? "Debug" : "Release") + "\n";
 		} else if (lines[i].find("$export_method") != -1) {
 		} else if (lines[i].find("$export_method") != -1) {
 			int export_method = p_preset->get(p_debug ? "application/export_method_debug" : "application/export_method_release");
 			int export_method = p_preset->get(p_debug ? "application/export_method_debug" : "application/export_method_release");
 			strnew += lines[i].replace("$export_method", export_method_string[export_method]) + "\n";
 			strnew += lines[i].replace("$export_method", export_method_string[export_method]) + "\n";
@@ -464,6 +468,20 @@ void EditorExportPlatformIOS::_fix_config_file(const Ref<EditorExportPreset> &p_
 			strnew += lines[i].replace("$godot_archs", p_config.architectures) + "\n";
 			strnew += lines[i].replace("$godot_archs", p_config.architectures) + "\n";
 		} else if (lines[i].find("$linker_flags") != -1) {
 		} else if (lines[i].find("$linker_flags") != -1) {
 			strnew += lines[i].replace("$linker_flags", p_config.linker_flags) + "\n";
 			strnew += lines[i].replace("$linker_flags", p_config.linker_flags) + "\n";
+		} else if (lines[i].find("$targeted_device_family") != -1) {
+			String xcode_value;
+			switch ((int)p_preset->get("application/targeted_device_family")) {
+				case 0: // iPhone
+					xcode_value = "1";
+					break;
+				case 1: // iPad
+					xcode_value = "2";
+					break;
+				case 2: // iPhone & iPad
+					xcode_value = "1,2";
+					break;
+			}
+			strnew += lines[i].replace("$targeted_device_family", xcode_value) + "\n";
 		} else if (lines[i].find("$cpp_code") != -1) {
 		} else if (lines[i].find("$cpp_code") != -1) {
 			strnew += lines[i].replace("$cpp_code", p_config.cpp_code) + "\n";
 			strnew += lines[i].replace("$cpp_code", p_config.cpp_code) + "\n";
 		} else if (lines[i].find("$docs_in_place") != -1) {
 		} else if (lines[i].find("$docs_in_place") != -1) {

+ 3 - 6
scene/2d/camera_2d.cpp

@@ -272,11 +272,10 @@ void Camera2D::_notification(int p_what) {
 			}
 			}
 
 
 			if (screen_drawing_enabled) {
 			if (screen_drawing_enabled) {
-				Color area_axis_color(0.5, 0.42, 0.87, 0.63);
+				Color area_axis_color(1, 0.4, 1, 0.63);
 				float area_axis_width = 1;
 				float area_axis_width = 1;
 				if (is_current()) {
 				if (is_current()) {
 					area_axis_width = 3;
 					area_axis_width = 3;
-					area_axis_color.a = 0.83;
 				}
 				}
 
 
 				Transform2D inv_camera_transform = get_camera_transform().affine_inverse();
 				Transform2D inv_camera_transform = get_camera_transform().affine_inverse();
@@ -297,10 +296,9 @@ void Camera2D::_notification(int p_what) {
 			}
 			}
 
 
 			if (limit_drawing_enabled) {
 			if (limit_drawing_enabled) {
-				Color limit_drawing_color(1, 1, 0, 0.63);
+				Color limit_drawing_color(1, 1, 0.25, 0.63);
 				float limit_drawing_width = 1;
 				float limit_drawing_width = 1;
 				if (is_current()) {
 				if (is_current()) {
-					limit_drawing_color.a = 0.83;
 					limit_drawing_width = 3;
 					limit_drawing_width = 3;
 				}
 				}
 
 
@@ -319,11 +317,10 @@ void Camera2D::_notification(int p_what) {
 			}
 			}
 
 
 			if (margin_drawing_enabled) {
 			if (margin_drawing_enabled) {
-				Color margin_drawing_color(0, 1, 1, 0.63);
+				Color margin_drawing_color(0.25, 1, 1, 0.63);
 				float margin_drawing_width = 1;
 				float margin_drawing_width = 1;
 				if (is_current()) {
 				if (is_current()) {
 					margin_drawing_width = 3;
 					margin_drawing_width = 3;
-					margin_drawing_color.a = 0.83;
 				}
 				}
 
 
 				Transform2D inv_camera_transform = get_camera_transform().affine_inverse();
 				Transform2D inv_camera_transform = get_camera_transform().affine_inverse();

+ 1 - 1
scene/3d/skeleton.cpp

@@ -666,7 +666,7 @@ void Skeleton::_rebuild_physical_bones_cache() {
 	const int b_size = bones.size();
 	const int b_size = bones.size();
 	for (int i = 0; i < b_size; ++i) {
 	for (int i = 0; i < b_size; ++i) {
 		PhysicalBone *parent_pb = _get_physical_bone_parent(i);
 		PhysicalBone *parent_pb = _get_physical_bone_parent(i);
-		if (parent_pb != bones[i].physical_bone) {
+		if (parent_pb != bones[i].cache_parent_physical_bone) {
 			bones.write[i].cache_parent_physical_bone = parent_pb;
 			bones.write[i].cache_parent_physical_bone = parent_pb;
 			if (bones[i].physical_bone) {
 			if (bones[i].physical_bone) {
 				bones[i].physical_bone->_on_bone_parent_changed();
 				bones[i].physical_bone->_on_bone_parent_changed();

+ 114 - 18
scene/gui/graph_node.cpp

@@ -32,6 +32,12 @@
 
 
 #include "core/method_bind_ext.gen.inc"
 #include "core/method_bind_ext.gen.inc"
 
 
+struct _MinSizeCache {
+	int min_size;
+	bool will_stretch;
+	int final_size;
+};
+
 bool GraphNode::_set(const StringName &p_name, const Variant &p_value) {
 bool GraphNode::_set(const StringName &p_name, const Variant &p_value) {
 	if (!p_name.operator String().begins_with("slot/")) {
 	if (!p_name.operator String().begins_with("slot/")) {
 		return false;
 		return false;
@@ -119,15 +125,23 @@ void GraphNode::_get_property_list(List<PropertyInfo> *p_list) const {
 }
 }
 
 
 void GraphNode::_resort() {
 void GraphNode::_resort() {
-	int sep = get_constant("separation");
+	/** First pass, determine minimum size AND amount of stretchable elements */
+
+	Size2 new_size = get_size();
 	Ref<StyleBox> sb = get_stylebox("frame");
 	Ref<StyleBox> sb = get_stylebox("frame");
-	bool first = true;
 
 
-	Size2 minsize;
+	int sep = get_constant("separation");
+
+	bool first = true;
+	int children_count = 0;
+	int stretch_min = 0;
+	int stretch_avail = 0;
+	float stretch_ratio_total = 0;
+	Map<Control *, _MinSizeCache> min_size_cache;
 
 
 	for (int i = 0; i < get_child_count(); i++) {
 	for (int i = 0; i < get_child_count(); i++) {
 		Control *c = Object::cast_to<Control>(get_child(i));
 		Control *c = Object::cast_to<Control>(get_child(i));
-		if (!c) {
+		if (!c || !c->is_visible_in_tree()) {
 			continue;
 			continue;
 		}
 		}
 		if (c->is_set_as_toplevel()) {
 		if (c->is_set_as_toplevel()) {
@@ -135,38 +149,120 @@ void GraphNode::_resort() {
 		}
 		}
 
 
 		Size2i size = c->get_combined_minimum_size();
 		Size2i size = c->get_combined_minimum_size();
+		_MinSizeCache msc;
 
 
-		minsize.y += size.y;
-		minsize.x = MAX(minsize.x, size.x);
+		stretch_min += size.height;
+		msc.min_size = size.height;
+		msc.will_stretch = c->get_v_size_flags() & SIZE_EXPAND;
 
 
-		if (first) {
-			first = false;
-		} else {
-			minsize.y += sep;
+		if (msc.will_stretch) {
+			stretch_avail += msc.min_size;
+			stretch_ratio_total += c->get_stretch_ratio();
 		}
 		}
+		msc.final_size = msc.min_size;
+		min_size_cache[c] = msc;
+		children_count++;
 	}
 	}
 
 
-	int vofs = 0;
-	int w = get_size().x - sb->get_minimum_size().x;
+	if (children_count == 0) {
+		return;
+	}
+
+	int stretch_max = new_size.height - (children_count - 1) * sep;
+	int stretch_diff = stretch_max - stretch_min;
+	if (stretch_diff < 0) {
+		//avoid negative stretch space
+		stretch_diff = 0;
+	}
+
+	stretch_avail += stretch_diff - sb->get_margin(MARGIN_BOTTOM) - sb->get_margin(MARGIN_TOP); //available stretch space.
+	/** Second, pass sucessively to discard elements that can't be stretched, this will run while stretchable
+		elements exist */
+
+	while (stretch_ratio_total > 0) { // first of all, don't even be here if no stretchable objects exist
+		bool refit_successful = true; //assume refit-test will go well
+
+		for (int i = 0; i < get_child_count(); i++) {
+			Control *c = Object::cast_to<Control>(get_child(i));
+			if (!c || !c->is_visible_in_tree()) {
+				continue;
+			}
+			if (c->is_set_as_toplevel()) {
+				continue;
+			}
 
 
+			ERR_FAIL_COND(!min_size_cache.has(c));
+			_MinSizeCache &msc = min_size_cache[c];
+
+			if (msc.will_stretch) { //wants to stretch
+				//let's see if it can really stretch
+
+				int final_pixel_size = stretch_avail * c->get_stretch_ratio() / stretch_ratio_total;
+				if (final_pixel_size < msc.min_size) {
+					//if available stretching area is too small for widget,
+					//then remove it from stretching area
+					msc.will_stretch = false;
+					stretch_ratio_total -= c->get_stretch_ratio();
+					refit_successful = false;
+					stretch_avail -= msc.min_size;
+					msc.final_size = msc.min_size;
+					break;
+				} else {
+					msc.final_size = final_pixel_size;
+				}
+			}
+		}
+
+		if (refit_successful) { //uf refit went well, break
+			break;
+		}
+	}
+
+	/** Final pass, draw and stretch elements **/
+
+	int ofs = sb->get_margin(MARGIN_TOP);
+
+	first = true;
+	int idx = 0;
 	cache_y.clear();
 	cache_y.clear();
+	int w = new_size.width - sb->get_minimum_size().x;
+
 	for (int i = 0; i < get_child_count(); i++) {
 	for (int i = 0; i < get_child_count(); i++) {
 		Control *c = Object::cast_to<Control>(get_child(i));
 		Control *c = Object::cast_to<Control>(get_child(i));
-		if (!c) {
+		if (!c || !c->is_visible_in_tree()) {
 			continue;
 			continue;
 		}
 		}
 		if (c->is_set_as_toplevel()) {
 		if (c->is_set_as_toplevel()) {
 			continue;
 			continue;
 		}
 		}
 
 
-		Size2i size = c->get_combined_minimum_size();
+		_MinSizeCache &msc = min_size_cache[c];
+
+		if (first) {
+			first = false;
+		} else {
+			ofs += sep;
+		}
 
 
-		Rect2 r(sb->get_margin(MARGIN_LEFT), sb->get_margin(MARGIN_TOP) + vofs, w, size.y);
+		int from = ofs;
+		int to = ofs + msc.final_size;
 
 
-		fit_child_in_rect(c, r);
-		cache_y.push_back(vofs + size.y * 0.5);
+		if (msc.will_stretch && idx == children_count - 1) {
+			//adjust so the last one always fits perfect
+			//compensating for numerical imprecision
 
 
-		vofs += size.y + sep;
+			to = new_size.height - sb->get_margin(MARGIN_BOTTOM);
+		}
+
+		int size = to - from;
+
+		Rect2 rect(sb->get_margin(MARGIN_LEFT), from, w, size);
+
+		fit_child_in_rect(c, rect);
+		cache_y.push_back(from - sb->get_margin(MARGIN_TOP) + size * 0.5);
+
+		ofs = to;
+		idx++;
 	}
 	}
 
 
 	update();
 	update();

+ 37 - 5
scene/gui/texture_button.cpp

@@ -166,9 +166,11 @@ void TextureButton::_notification(int p_what) {
 				} break;
 				} break;
 			}
 			}
 
 
+			Point2 ofs;
+			Size2 size;
+
 			if (texdraw.is_valid()) {
 			if (texdraw.is_valid()) {
-				Point2 ofs;
-				Size2 size = texdraw->get_size();
+				size = texdraw->get_size();
 				_texture_region = Rect2(Point2(), texdraw->get_size());
 				_texture_region = Rect2(Point2(), texdraw->get_size());
 				_tile = false;
 				_tile = false;
 				if (expand) {
 				if (expand) {
@@ -218,17 +220,21 @@ void TextureButton::_notification(int p_what) {
 				}
 				}
 
 
 				_position_rect = Rect2(ofs, size);
 				_position_rect = Rect2(ofs, size);
+
+				size.width *= hflip ? -1.0f : 1.0f;
+				size.height *= vflip ? -1.0f : 1.0f;
+
 				if (_tile) {
 				if (_tile) {
-					draw_texture_rect(texdraw, _position_rect, _tile);
+					draw_texture_rect(texdraw, Rect2(ofs, size), _tile);
 				} else {
 				} else {
-					draw_texture_rect_region(texdraw, _position_rect, _texture_region);
+					draw_texture_rect_region(texdraw, Rect2(ofs, size), _texture_region);
 				}
 				}
 			} else {
 			} else {
 				_position_rect = Rect2();
 				_position_rect = Rect2();
 			}
 			}
 
 
 			if (has_focus() && focused.is_valid()) {
 			if (has_focus() && focused.is_valid()) {
-				draw_texture_rect(focused, _position_rect, false);
+				draw_texture_rect(focused, Rect2(ofs, size), false);
 			};
 			};
 		} break;
 		} break;
 	}
 	}
@@ -243,6 +249,10 @@ void TextureButton::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("set_click_mask", "mask"), &TextureButton::set_click_mask);
 	ClassDB::bind_method(D_METHOD("set_click_mask", "mask"), &TextureButton::set_click_mask);
 	ClassDB::bind_method(D_METHOD("set_expand", "p_expand"), &TextureButton::set_expand);
 	ClassDB::bind_method(D_METHOD("set_expand", "p_expand"), &TextureButton::set_expand);
 	ClassDB::bind_method(D_METHOD("set_stretch_mode", "p_mode"), &TextureButton::set_stretch_mode);
 	ClassDB::bind_method(D_METHOD("set_stretch_mode", "p_mode"), &TextureButton::set_stretch_mode);
+	ClassDB::bind_method(D_METHOD("set_flip_h", "enable"), &TextureButton::set_flip_h);
+	ClassDB::bind_method(D_METHOD("is_flipped_h"), &TextureButton::is_flipped_h);
+	ClassDB::bind_method(D_METHOD("set_flip_v", "enable"), &TextureButton::set_flip_v);
+	ClassDB::bind_method(D_METHOD("is_flipped_v"), &TextureButton::is_flipped_v);
 
 
 	ClassDB::bind_method(D_METHOD("get_normal_texture"), &TextureButton::get_normal_texture);
 	ClassDB::bind_method(D_METHOD("get_normal_texture"), &TextureButton::get_normal_texture);
 	ClassDB::bind_method(D_METHOD("get_pressed_texture"), &TextureButton::get_pressed_texture);
 	ClassDB::bind_method(D_METHOD("get_pressed_texture"), &TextureButton::get_pressed_texture);
@@ -262,6 +272,8 @@ void TextureButton::_bind_methods() {
 	ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture_click_mask", PROPERTY_HINT_RESOURCE_TYPE, "BitMap"), "set_click_mask", "get_click_mask");
 	ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture_click_mask", PROPERTY_HINT_RESOURCE_TYPE, "BitMap"), "set_click_mask", "get_click_mask");
 	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "expand", PROPERTY_HINT_RESOURCE_TYPE, "bool"), "set_expand", "get_expand");
 	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "expand", PROPERTY_HINT_RESOURCE_TYPE, "bool"), "set_expand", "get_expand");
 	ADD_PROPERTY(PropertyInfo(Variant::INT, "stretch_mode", PROPERTY_HINT_ENUM, "Scale,Tile,Keep,Keep Centered,Keep Aspect,Keep Aspect Centered,Keep Aspect Covered"), "set_stretch_mode", "get_stretch_mode");
 	ADD_PROPERTY(PropertyInfo(Variant::INT, "stretch_mode", PROPERTY_HINT_ENUM, "Scale,Tile,Keep,Keep Centered,Keep Aspect,Keep Aspect Centered,Keep Aspect Covered"), "set_stretch_mode", "get_stretch_mode");
+	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "flip_h", PROPERTY_HINT_RESOURCE_TYPE, "bool"), "set_flip_h", "is_flipped_h");
+	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "flip_v", PROPERTY_HINT_RESOURCE_TYPE, "bool"), "set_flip_v", "is_flipped_v");
 
 
 	BIND_ENUM_CONSTANT(STRETCH_SCALE);
 	BIND_ENUM_CONSTANT(STRETCH_SCALE);
 	BIND_ENUM_CONSTANT(STRETCH_TILE);
 	BIND_ENUM_CONSTANT(STRETCH_TILE);
@@ -338,9 +350,29 @@ TextureButton::StretchMode TextureButton::get_stretch_mode() const {
 	return stretch_mode;
 	return stretch_mode;
 }
 }
 
 
+void TextureButton::set_flip_h(bool p_flip) {
+	hflip = p_flip;
+	update();
+}
+
+bool TextureButton::is_flipped_h() const {
+	return hflip;
+}
+
+void TextureButton::set_flip_v(bool p_flip) {
+	vflip = p_flip;
+	update();
+}
+
+bool TextureButton::is_flipped_v() const {
+	return vflip;
+}
+
 TextureButton::TextureButton() {
 TextureButton::TextureButton() {
 	expand = false;
 	expand = false;
 	stretch_mode = STRETCH_SCALE;
 	stretch_mode = STRETCH_SCALE;
+	hflip = false;
+	vflip = false;
 
 
 	_texture_region = Rect2();
 	_texture_region = Rect2();
 	_position_rect = Rect2();
 	_position_rect = Rect2();

+ 9 - 0
scene/gui/texture_button.h

@@ -61,6 +61,9 @@ private:
 	Rect2 _position_rect;
 	Rect2 _position_rect;
 	bool _tile;
 	bool _tile;
 
 
+	bool hflip;
+	bool vflip;
+
 protected:
 protected:
 	virtual Size2 get_minimum_size() const;
 	virtual Size2 get_minimum_size() const;
 	virtual bool has_point(const Point2 &p_point) const;
 	virtual bool has_point(const Point2 &p_point) const;
@@ -88,6 +91,12 @@ public:
 	void set_stretch_mode(StretchMode p_stretch_mode);
 	void set_stretch_mode(StretchMode p_stretch_mode);
 	StretchMode get_stretch_mode() const;
 	StretchMode get_stretch_mode() const;
 
 
+	void set_flip_h(bool p_flip);
+	bool is_flipped_h() const;
+
+	void set_flip_v(bool p_flip);
+	bool is_flipped_v() const;
+
 	TextureButton();
 	TextureButton();
 };
 };
 
 

+ 11 - 9
scene/main/http_request.cpp

@@ -322,17 +322,19 @@ bool HTTPRequest::_update_connection() {
 			}
 			}
 
 
 			PoolByteArray chunk = client->read_response_body_chunk();
 			PoolByteArray chunk = client->read_response_body_chunk();
-			downloaded.add(chunk.size());
 
 
-			if (file) {
-				PoolByteArray::Read r = chunk.read();
-				file->store_buffer(r.ptr(), chunk.size());
-				if (file->get_error() != OK) {
-					call_deferred("_request_done", RESULT_DOWNLOAD_FILE_WRITE_ERROR, response_code, response_headers, PoolByteArray());
-					return true;
+			if (chunk.size()) {
+				downloaded.add(chunk.size());
+				if (file) {
+					PoolByteArray::Read r = chunk.read();
+					file->store_buffer(r.ptr(), chunk.size());
+					if (file->get_error() != OK) {
+						call_deferred("_request_done", RESULT_DOWNLOAD_FILE_WRITE_ERROR, response_code, response_headers, PoolByteArray());
+						return true;
+					}
+				} else {
+					body.append_array(chunk);
 				}
 				}
-			} else {
-				body.append_array(chunk);
 			}
 			}
 
 
 			if (body_size_limit >= 0 && downloaded.get() > body_size_limit) {
 			if (body_size_limit >= 0 && downloaded.get() > body_size_limit) {

+ 14 - 9
servers/physics/body_sw.cpp

@@ -65,16 +65,18 @@ void BodySW::update_inertias() {
 			// We have to recompute the center of mass.
 			// We have to recompute the center of mass.
 			center_of_mass_local.zero();
 			center_of_mass_local.zero();
 
 
-			for (int i = 0; i < get_shape_count(); i++) {
-				real_t area = get_shape_area(i);
+			if (total_area != 0.0) {
+				for (int i = 0; i < get_shape_count(); i++) {
+					real_t area = get_shape_area(i);
 
 
-				real_t mass = area * this->mass / total_area;
+					real_t mass = area * this->mass / total_area;
 
 
-				// NOTE: we assume that the shape origin is also its center of mass.
-				center_of_mass_local += mass * get_shape_transform(i).origin;
-			}
+					// NOTE: we assume that the shape origin is also its center of mass.
+					center_of_mass_local += mass * get_shape_transform(i).origin;
+				}
 
 
-			center_of_mass_local /= mass;
+				center_of_mass_local /= mass;
+			}
 
 
 			// Recompute the inertia tensor.
 			// Recompute the inertia tensor.
 			Basis inertia_tensor;
 			Basis inertia_tensor;
@@ -86,12 +88,15 @@ void BodySW::update_inertias() {
 					continue;
 					continue;
 				}
 				}
 
 
+				real_t area = get_shape_area(i);
+				if (area == 0.0) {
+					continue;
+				}
+
 				inertia_set = true;
 				inertia_set = true;
 
 
 				const ShapeSW *shape = get_shape(i);
 				const ShapeSW *shape = get_shape(i);
 
 
-				real_t area = get_shape_area(i);
-
 				real_t mass = area * this->mass / total_area;
 				real_t mass = area * this->mass / total_area;
 
 
 				Basis shape_inertia_tensor = shape->get_moment_of_inertia(mass).to_diagonal_matrix();
 				Basis shape_inertia_tensor = shape->get_moment_of_inertia(mass).to_diagonal_matrix();