Sfoglia il codice sorgente

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

Rémi Verschelde 4 anni fa
parent
commit
ea6ca86776

+ 8 - 0
drivers/pulseaudio/audio_driver_pulseaudio.cpp

@@ -35,6 +35,10 @@
 #include "core/os/os.h"
 #include "core/project_settings.h"
 
+#ifdef ALSAMIDI_ENABLED
+#include "drivers/alsa/asound-so_wrap.h"
+#endif
+
 void AudioDriverPulseAudio::pa_state_cb(pa_context *c, void *userdata) {
 	AudioDriverPulseAudio *ad = (AudioDriverPulseAudio *)userdata;
 
@@ -271,6 +275,10 @@ Error AudioDriverPulseAudio::init() {
 	int dylibloader_verbose = 1;
 #else
 	int dylibloader_verbose = 0;
+#endif
+#ifdef ALSAMIDI_ENABLED
+	// If using PulseAudio with ALSA MIDI, we need to initialize ALSA as well
+	initialize_asound(dylibloader_verbose);
 #endif
 	if (initialize_pulse(dylibloader_verbose)) {
 		return ERR_CANT_OPEN;

+ 45 - 25
editor/animation_track_editor_plugins.cpp

@@ -94,47 +94,67 @@ void AnimationTrackEditColor::draw_key_link(int p_index, float p_pixels_sec, int
 	Ref<Font> font = get_font("font", "Label");
 	int fh = (font->get_height() * 0.8);
 
+	fh /= 3;
+
 	int x_from = p_x + fh / 2 - 1;
 	int x_to = p_next_x - fh / 2 + 1;
-	fh /= 3;
+	x_from = MAX(x_from, p_clip_left);
+	x_to = MIN(x_to, p_clip_right);
+
+	int y_from = (get_size().height - fh) / 2;
 
 	if (x_from > p_clip_right || x_to < p_clip_left) {
 		return;
 	}
 
-	Color color = get_animation()->track_get_key_value(get_track(), p_index);
-	Color color_next = get_animation()->track_get_key_value(get_track(), p_index + 1);
+	Vector<Color> color_samples;
+	color_samples.push_back(get_animation()->track_get_key_value(get_track(), p_index));
 
-	if (x_from < p_clip_left) {
-		float c = float(p_clip_left - x_from) / (x_to - x_from);
-		color = color.linear_interpolate(color_next, c);
-		x_from = p_clip_left;
-	}
+	if (get_animation()->track_get_type(get_track()) == Animation::TYPE_VALUE) {
+		if (get_animation()->track_get_interpolation_type(get_track()) != Animation::INTERPOLATION_NEAREST &&
+				(get_animation()->value_track_get_update_mode(get_track()) == Animation::UPDATE_CONTINUOUS ||
+						get_animation()->value_track_get_update_mode(get_track()) == Animation::UPDATE_CAPTURE) &&
+				!Math::is_zero_approx(get_animation()->track_get_key_transition(get_track(), p_index))) {
+			float start_time = get_animation()->track_get_key_time(get_track(), p_index);
+			float end_time = get_animation()->track_get_key_time(get_track(), p_index + 1);
 
-	if (x_to > p_clip_right) {
-		float c = float(p_clip_right - x_from) / (x_to - x_from);
-		color_next = color.linear_interpolate(color_next, c);
-		x_to = p_clip_right;
-	}
+			Color color_next = get_animation()->value_track_interpolate(get_track(), end_time);
 
-	int y_from = (get_size().height - fh) / 2;
+			if (!color_samples[0].is_equal_approx(color_next)) {
+				color_samples.resize(1 + (x_to - x_from) / 64); // Make a color sample every 64 px.
+				for (int i = 1; i < color_samples.size(); i++) {
+					float j = i;
+					color_samples.write[i] = get_animation()->value_track_interpolate(
+							get_track(),
+							Math::lerp(start_time, end_time, j / color_samples.size()));
+				}
+			}
+			color_samples.push_back(color_next);
+		} else {
+			color_samples.push_back(color_samples[0]);
+		}
+	} else {
+		color_samples.push_back(get_animation()->track_get_key_value(get_track(), p_index + 1));
+	}
 
-	Vector<Vector2> points;
-	Vector<Color> colors;
+	for (int i = 0; i < color_samples.size() - 1; i++) {
+		Vector<Vector2> points;
+		Vector<Color> colors;
 
-	points.push_back(Vector2(x_from, y_from));
-	colors.push_back(color);
+		points.push_back(Vector2(Math::lerp(x_from, x_to, float(i) / (color_samples.size() - 1)), y_from));
+		colors.push_back(color_samples[i]);
 
-	points.push_back(Vector2(x_to, y_from));
-	colors.push_back(color_next);
+		points.push_back(Vector2(Math::lerp(x_from, x_to, float(i + 1) / (color_samples.size() - 1)), y_from));
+		colors.push_back(color_samples[i + 1]);
 
-	points.push_back(Vector2(x_to, y_from + fh));
-	colors.push_back(color_next);
+		points.push_back(Vector2(Math::lerp(x_from, x_to, float(i + 1) / (color_samples.size() - 1)), y_from + fh));
+		colors.push_back(color_samples[i + 1]);
 
-	points.push_back(Vector2(x_from, y_from + fh));
-	colors.push_back(color);
+		points.push_back(Vector2(Math::lerp(x_from, x_to, float(i) / (color_samples.size() - 1)), y_from + fh));
+		colors.push_back(color_samples[i]);
 
-	draw_primitive(points, colors, Vector<Vector2>());
+		draw_primitive(points, colors, Vector<Vector2>());
+	}
 }
 
 void AnimationTrackEditColor::draw_key(int p_index, float p_pixels_sec, int p_x, bool p_selected, int p_clip_left, int p_clip_right) {

+ 88 - 43
editor/editor_feature_profile.cpp

@@ -45,6 +45,16 @@ const char *EditorFeatureProfile::feature_names[FEATURE_MAX] = {
 	TTRC("Import Dock"),
 };
 
+const char *EditorFeatureProfile::feature_descriptions[FEATURE_MAX] = {
+	TTRC("Allows to view and edit 3D scenes."),
+	TTRC("Allows to edit scripts using the integrated script editor."),
+	TTRC("Provides built-in access to the Asset Library."),
+	TTRC("Allows editing the node hierarchy in the Scene dock."),
+	TTRC("Allows to work with signals and groups of the node selected in the Scene dock."),
+	TTRC("Allows to browse the local file system via a dedicated dock."),
+	TTRC("Allows to configure import settings for individual assets. Requires the FileSystem dock to function."),
+};
+
 const char *EditorFeatureProfile::feature_identifiers[FEATURE_MAX] = {
 	"3d",
 	"script",
@@ -142,6 +152,11 @@ String EditorFeatureProfile::get_feature_name(Feature p_feature) {
 	return feature_names[p_feature];
 }
 
+String EditorFeatureProfile::get_feature_description(Feature p_feature) {
+	ERR_FAIL_INDEX_V(p_feature, FEATURE_MAX, String());
+	return feature_descriptions[p_feature];
+}
+
 Error EditorFeatureProfile::save_to_file(const String &p_path) {
 	Dictionary json;
 	json["type"] = "feature_profile";
@@ -416,7 +431,7 @@ void EditorFeatureProfileManager::_profile_action(int p_action) {
 			export_profile->set_current_file(_get_selected_profile() + ".profile");
 		} break;
 		case PROFILE_NEW: {
-			new_profile_dialog->popup_centered_minsize();
+			new_profile_dialog->popup_centered(Size2(240, 60) * EDSCALE);
 			new_profile_name->clear();
 			new_profile_name->grab_focus();
 		} break;
@@ -424,8 +439,8 @@ void EditorFeatureProfileManager::_profile_action(int p_action) {
 			String selected = _get_selected_profile();
 			ERR_FAIL_COND(selected == String());
 
-			erase_profile_dialog->set_text(vformat(TTR("Erase profile '%s'? (no undo)"), selected));
-			erase_profile_dialog->popup_centered_minsize();
+			erase_profile_dialog->set_text(vformat(TTR("Remove currently selected profile, '%s'? Cannot be undone."), selected));
+			erase_profile_dialog->popup_centered(Size2(240, 60) * EDSCALE);
 		} break;
 	}
 }
@@ -529,12 +544,28 @@ void EditorFeatureProfileManager::_class_list_item_selected() {
 	}
 
 	Variant md = item->get_metadata(0);
-	if (md.get_type() != Variant::STRING) {
+	if (md.get_type() == Variant::STRING) {
+		String class_name = md;
+		String class_description;
+
+		DocData *dd = EditorHelp::get_doc_data();
+		Map<String, DocData::ClassDoc>::Element *E = dd->class_list.find(class_name);
+		if (E) {
+			class_description = E->get().brief_description;
+		}
+
+		description_bit->set_text(class_description);
+	} else if (md.get_type() == Variant::INT) {
+		int feature_id = md;
+		String feature_description = EditorFeatureProfile::get_feature_description(EditorFeatureProfile::Feature(feature_id));
+
+		description_bit->set_text(feature_description);
+		return;
+	} else {
 		return;
 	}
 
 	String class_name = md;
-
 	if (edited->is_class_disabled(class_name)) {
 		return;
 	}
@@ -554,27 +585,28 @@ void EditorFeatureProfileManager::_class_list_item_selected() {
 		option->set_metadata(0, CLASS_OPTION_DISABLE_EDITOR);
 	}
 
-	TreeItem *properties = property_list->create_item(root);
-	properties->set_text(0, TTR("Enabled Properties:"));
-
 	List<PropertyInfo> props;
-
 	ClassDB::get_property_list(class_name, &props, true);
 
-	for (List<PropertyInfo>::Element *E = props.front(); E; E = E->next()) {
-		String name = E->get().name;
-		if (!(E->get().usage & PROPERTY_USAGE_EDITOR)) {
-			continue;
+	if (props.size() > 0) {
+		TreeItem *properties = property_list->create_item(root);
+		properties->set_text(0, TTR("Class Properties:"));
+
+		for (List<PropertyInfo>::Element *E = props.front(); E; E = E->next()) {
+			String name = E->get().name;
+			if (!(E->get().usage & PROPERTY_USAGE_EDITOR)) {
+				continue;
+			}
+			TreeItem *property = property_list->create_item(properties);
+			property->set_cell_mode(0, TreeItem::CELL_MODE_CHECK);
+			property->set_editable(0, true);
+			property->set_selectable(0, true);
+			property->set_checked(0, !edited->is_class_property_disabled(class_name, name));
+			property->set_text(0, name.capitalize());
+			property->set_metadata(0, name);
+			String icon_type = Variant::get_type_name(E->get().type);
+			property->set_icon(0, EditorNode::get_singleton()->get_class_icon(icon_type));
 		}
-		TreeItem *property = property_list->create_item(properties);
-		property->set_cell_mode(0, TreeItem::CELL_MODE_CHECK);
-		property->set_editable(0, true);
-		property->set_selectable(0, true);
-		property->set_checked(0, !edited->is_class_property_disabled(class_name, name));
-		property->set_text(0, name.capitalize());
-		property->set_metadata(0, name);
-		String icon_type = Variant::get_type_name(E->get().type);
-		property->set_icon(0, EditorNode::get_singleton()->get_class_icon(icon_type));
 	}
 
 	updating_features = false;
@@ -704,7 +736,7 @@ void EditorFeatureProfileManager::_update_selected_profile() {
 
 	TreeItem *features = class_list->create_item(root);
 	TreeItem *last_feature;
-	features->set_text(0, TTR("Enabled Features:"));
+	features->set_text(0, TTR("Main Features") + ":");
 	for (int i = 0; i < EditorFeatureProfile::FEATURE_MAX; i++) {
 		TreeItem *feature;
 		if (i == EditorFeatureProfile::FEATURE_IMPORT_DOCK) {
@@ -728,7 +760,7 @@ void EditorFeatureProfileManager::_update_selected_profile() {
 	}
 
 	TreeItem *classes = class_list->create_item(root);
-	classes->set_text(0, TTR("Enabled Classes:"));
+	classes->set_text(0, TTR("Nodes and Classes") + ":");
 
 	_fill_classes_from(classes, "Node", class_selected);
 	_fill_classes_from(classes, "Resource", class_selected);
@@ -832,47 +864,51 @@ EditorFeatureProfileManager::EditorFeatureProfileManager() {
 	name_hbc->add_child(current_profile_name);
 	current_profile_name->set_editable(false);
 	current_profile_name->set_h_size_flags(SIZE_EXPAND_FILL);
-	profile_actions[PROFILE_CLEAR] = memnew(Button(TTR("Unset")));
+	profile_actions[PROFILE_CLEAR] = memnew(Button(TTR("Reset to Default")));
 	name_hbc->add_child(profile_actions[PROFILE_CLEAR]);
 	profile_actions[PROFILE_CLEAR]->set_disabled(true);
 	profile_actions[PROFILE_CLEAR]->connect("pressed", this, "_profile_action", varray(PROFILE_CLEAR));
 
 	main_vbc->add_margin_child(TTR("Current Profile:"), name_hbc);
 
+	main_vbc->add_child(memnew(HSeparator));
+
 	HBoxContainer *profiles_hbc = memnew(HBoxContainer);
 	profile_list = memnew(OptionButton);
 	profile_list->set_h_size_flags(SIZE_EXPAND_FILL);
 	profiles_hbc->add_child(profile_list);
 	profile_list->connect("item_selected", this, "_profile_selected");
 
-	profile_actions[PROFILE_SET] = memnew(Button(TTR("Make Current")));
-	profiles_hbc->add_child(profile_actions[PROFILE_SET]);
-	profile_actions[PROFILE_SET]->set_disabled(true);
-	profile_actions[PROFILE_SET]->connect("pressed", this, "_profile_action", varray(PROFILE_SET));
+	profile_actions[PROFILE_NEW] = memnew(Button(TTR("Create Profile")));
+	profiles_hbc->add_child(profile_actions[PROFILE_NEW]);
+	profile_actions[PROFILE_NEW]->connect("pressed", this, "_profile_action", varray(PROFILE_NEW));
 
-	profile_actions[PROFILE_ERASE] = memnew(Button(TTR("Remove")));
+	profile_actions[PROFILE_ERASE] = memnew(Button(TTR("Remove Profile")));
 	profiles_hbc->add_child(profile_actions[PROFILE_ERASE]);
 	profile_actions[PROFILE_ERASE]->set_disabled(true);
 	profile_actions[PROFILE_ERASE]->connect("pressed", this, "_profile_action", varray(PROFILE_ERASE));
 
-	profiles_hbc->add_child(memnew(VSeparator));
+	main_vbc->add_margin_child(TTR("Available Profiles:"), profiles_hbc);
 
-	profile_actions[PROFILE_NEW] = memnew(Button(TTR("New")));
-	profiles_hbc->add_child(profile_actions[PROFILE_NEW]);
-	profile_actions[PROFILE_NEW]->connect("pressed", this, "_profile_action", varray(PROFILE_NEW));
+	HBoxContainer *current_profile_hbc = memnew(HBoxContainer);
+
+	profile_actions[PROFILE_SET] = memnew(Button(TTR("Make Current")));
+	current_profile_hbc->add_child(profile_actions[PROFILE_SET]);
+	profile_actions[PROFILE_SET]->set_disabled(true);
+	profile_actions[PROFILE_SET]->connect("pressed", this, "_profile_action", varray(PROFILE_SET));
 
-	profiles_hbc->add_child(memnew(VSeparator));
+	current_profile_hbc->add_child(memnew(VSeparator));
 
 	profile_actions[PROFILE_IMPORT] = memnew(Button(TTR("Import")));
-	profiles_hbc->add_child(profile_actions[PROFILE_IMPORT]);
+	current_profile_hbc->add_child(profile_actions[PROFILE_IMPORT]);
 	profile_actions[PROFILE_IMPORT]->connect("pressed", this, "_profile_action", varray(PROFILE_IMPORT));
 
 	profile_actions[PROFILE_EXPORT] = memnew(Button(TTR("Export")));
-	profiles_hbc->add_child(profile_actions[PROFILE_EXPORT]);
+	current_profile_hbc->add_child(profile_actions[PROFILE_EXPORT]);
 	profile_actions[PROFILE_EXPORT]->set_disabled(true);
 	profile_actions[PROFILE_EXPORT]->connect("pressed", this, "_profile_action", varray(PROFILE_EXPORT));
 
-	main_vbc->add_margin_child(TTR("Available Profiles:"), profiles_hbc);
+	main_vbc->add_child(current_profile_hbc);
 
 	h_split = memnew(HSplitContainer);
 	h_split->set_v_size_flags(SIZE_EXPAND_FILL);
@@ -883,7 +919,7 @@ EditorFeatureProfileManager::EditorFeatureProfileManager() {
 	class_list_vbc->set_h_size_flags(SIZE_EXPAND_FILL);
 
 	class_list = memnew(Tree);
-	class_list_vbc->add_margin_child(TTR("Enabled Classes:"), class_list, true);
+	class_list_vbc->add_margin_child(TTR("Configure Selected Profile") + ":", class_list, true);
 	class_list->set_hide_root(true);
 	class_list->set_edit_checkbox_cell_only_when_checkbox_is_pressed(true);
 	class_list->connect("cell_selected", this, "_class_list_item_selected");
@@ -894,17 +930,26 @@ EditorFeatureProfileManager::EditorFeatureProfileManager() {
 	h_split->add_child(property_list_vbc);
 	property_list_vbc->set_h_size_flags(SIZE_EXPAND_FILL);
 
+	description_bit = memnew(EditorHelpBit);
+	property_list_vbc->add_margin_child(TTR("Description") + ":", description_bit, false);
+	description_bit->set_custom_minimum_size(Size2(0, 80) * EDSCALE);
+
 	property_list = memnew(Tree);
-	property_list_vbc->add_margin_child(TTR("Class Options"), property_list, true);
+	property_list_vbc->add_margin_child(TTR("Extra Options") + ":", property_list, true);
 	property_list->set_hide_root(true);
 	property_list->set_hide_folding(true);
 	property_list->set_edit_checkbox_cell_only_when_checkbox_is_pressed(true);
 	property_list->connect("item_edited", this, "_property_item_edited", varray(), CONNECT_DEFERRED);
 
 	new_profile_dialog = memnew(ConfirmationDialog);
-	new_profile_dialog->set_title(TTR("New profile name:"));
+	new_profile_dialog->set_title(TTR("Create Profile"));
+	VBoxContainer *new_profile_vb = memnew(VBoxContainer);
+	new_profile_dialog->add_child(new_profile_vb);
+	Label *new_profile_label = memnew(Label);
+	new_profile_label->set_text(TTR("New profile name") + ":");
+	new_profile_vb->add_child(new_profile_label);
 	new_profile_name = memnew(LineEdit);
-	new_profile_dialog->add_child(new_profile_name);
+	new_profile_vb->add_child(new_profile_name);
 	new_profile_name->set_custom_minimum_size(Size2(300 * EDSCALE, 1));
 	add_child(new_profile_dialog);
 	new_profile_dialog->connect("confirmed", this, "_create_new_profile");
@@ -913,7 +958,7 @@ EditorFeatureProfileManager::EditorFeatureProfileManager() {
 
 	erase_profile_dialog = memnew(ConfirmationDialog);
 	add_child(erase_profile_dialog);
-	erase_profile_dialog->set_title(TTR("Erase Profile"));
+	erase_profile_dialog->set_title(TTR("Remove Profile"));
 	erase_profile_dialog->connect("confirmed", this, "_erase_selected_profile");
 
 	import_profiles = memnew(EditorFileDialog);

+ 4 - 0
editor/editor_feature_profile.h

@@ -34,6 +34,7 @@
 #include "core/os/file_access.h"
 #include "core/reference.h"
 #include "editor/editor_file_dialog.h"
+#include "editor_help.h"
 #include "scene/gui/dialogs.h"
 #include "scene/gui/option_button.h"
 #include "scene/gui/separator.h"
@@ -64,6 +65,7 @@ private:
 
 	bool features_disabled[FEATURE_MAX];
 	static const char *feature_names[FEATURE_MAX];
+	static const char *feature_descriptions[FEATURE_MAX];
 	static const char *feature_identifiers[FEATURE_MAX];
 
 	String _get_feature_name(Feature p_feature) { return get_feature_name(p_feature); }
@@ -92,6 +94,7 @@ public:
 	Error load_from_file(const String &p_path);
 
 	static String get_feature_name(Feature p_feature);
+	static String get_feature_description(Feature p_feature);
 
 	EditorFeatureProfile();
 };
@@ -127,6 +130,7 @@ class EditorFeatureProfileManager : public AcceptDialog {
 
 	Tree *class_list;
 	Tree *property_list;
+	EditorHelpBit *description_bit;
 
 	EditorFileDialog *import_profiles;
 	EditorFileDialog *export_profile;

+ 1 - 1
editor/icons/icon_gradient.svg

@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><linearGradient id="a" gradientUnits="userSpaceOnUse" x1="10" x2="10" y1="1" y2="15"><stop offset="0" stop-color="#e0e0e0"/><stop offset="1" stop-color="#e0e0e0" stop-opacity="0"/></linearGradient><path d="m2 1c-.55228 0-1 .44772-1 1v12c0 .55228.44772 1 1 1h12c.55228 0 1-.44772 1-1v-12c0-.55228-.44772-1-1-1z" fill="url(#a)"/></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g fill="#e0e0e0"><path d="m3 1c-1.1046 0-2 .8954299-2 1.9999999v10.0000001c0 1.1046.89543 2 2 2h10c1.1046 0 2-.89543 2-2v-10.0000001c0-1.1046-.89543-1.9999999-2-1.9999999zm0 1.9999999h10v10.0000001h-10z"/><path d="m7.5 5.500001h1v1h-1z" stroke-width=".787342"/><path d="m3.5 3.5v9h3v-1h1v-1h-1v-1h1v-1h-1v-1h1v-1h-1v-1h1v-1h-1v-1z" stroke-width="4.09116"/><g stroke-width=".787342"><path d="m7.5 3.5h1v1h-1z"/><path d="m7.5 9.500001h1v1h-1z"/><path d="m7.5 7.500001h1v1h-1z"/><path d="m7.5 11.5h1v1h-1z"/><path d="m8.5 4.500001h1v1h-1z"/><path d="m8.5 6.500001h1v1h-1z"/><path d="m8.5 8.500002h1v1h-1z"/><path d="m8.5 10.500002h1v1h-1z"/></g></g></svg>

+ 1 - 1
editor/icons/icon_gradient_texture.svg

@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><linearGradient id="a" gradientUnits="userSpaceOnUse" x1="10" x2="10" y1="1" y2="15"><stop offset="0" stop-color="#e0e0e0"/><stop offset="1" stop-color="#e0e0e0" stop-opacity="0"/></linearGradient><g transform="translate(0 -1036.4)"><path d="m2 1a1 1 0 0 0 -1 1v12a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-12a1 1 0 0 0 -1-1zm1 2h10v8h-10z" fill="url(#a)" transform="translate(0 1036.4)"/><g fill="#e0e0e0"><path d="m6 1043.4h2v1h-2z"/><path d="m6 1044.4h2v2h-2z"/><path d="m4 1045.4h2v1h-2z"/><path d="m8 1044.4h2v2h-2z"/><path d="m10 1044.4h2v2h-2z"/><path d="m8 1042.4h3v2h-3z"/><path d="m9 1041.4h1v1h-1z"/><path d="m5 1044.4h1v1h-1z"/></g></g></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g fill="#e0e0e0"><path d="m12.5 10.5v-7h-3v1h-1v1h1v1h-1v1h1v1h-1v1h1v1zm-4-1h-1v1h1zm-1 0v-1h-1v1zm0-1h1v-1h-1zm0-1v-1h-1v1zm0-1h1v-1h-1zm0-1v-1h-1v1zm0-1h1v-1h-1z" stroke-width=".787342"/><path d="m2 1c-.552285 0-1 .4477153-1 1v12.000001c0 .552285.447715 1 1 1h11.999999c.552285 0 1-.447715 1-1v-12.000001c0-.5522847-.447715-1-1-1zm1 2.0000001h9.999999v8.0000009h-9.999999z" fill-opacity=".99608"/></g></svg>

+ 0 - 1
editor/plugins/texture_region_editor_plugin.cpp

@@ -951,7 +951,6 @@ void TextureRegionEditor::_edit_region() {
 	if (cache_map.has(texture->get_rid())) {
 		autoslice_cache = cache_map[texture->get_rid()];
 		autoslice_is_dirty = false;
-		return;
 	} else {
 		if (is_visible() && snap_mode == SNAP_AUTOSLICE) {
 			_update_autoslice();

+ 14 - 0
platform/android/export/export.cpp

@@ -2100,6 +2100,13 @@ public:
 		// Validate the rest of the configuration.
 
 		String dk = p_preset->get("keystore/debug");
+		String dk_user = p_preset->get("keystore/debug_user");
+		String dk_password = p_preset->get("keystore/debug_password");
+
+		if ((dk.empty() || dk_user.empty() || dk_password.empty()) && (!dk.empty() || !dk_user.empty() || !dk_password.empty())) {
+			valid = false;
+			err += TTR("Either Debug Keystore, Debug User AND Debug Password settings must be configured OR none of them.") + "\n";
+		}
 
 		if (!FileAccess::exists(dk)) {
 			dk = EditorSettings::get_singleton()->get("export/android/debug_keystore");
@@ -2110,6 +2117,13 @@ public:
 		}
 
 		String rk = p_preset->get("keystore/release");
+		String rk_user = p_preset->get("keystore/release_user");
+		String rk_password = p_preset->get("keystore/release_password");
+
+		if ((rk.empty() || rk_user.empty() || rk_password.empty()) && (!rk.empty() || !rk_user.empty() || !rk_password.empty())) {
+			valid = false;
+			err += TTR("Either Release Keystore, Release User AND Release Password settings must be configured OR none of them.") + "\n";
+		}
 
 		if (!rk.empty() && !FileAccess::exists(rk)) {
 			valid = false;