Browse Source

Merge pull request #59328 from raulsntos/csharp-flag-enums-3.x

Rémi Verschelde 3 years ago
parent
commit
1b7037807f

+ 16 - 17
editor/editor_properties.cpp

@@ -628,14 +628,12 @@ EditorPropertyEnum::EditorPropertyEnum() {
 
 ///////////////////// FLAGS /////////////////////////
 
-void EditorPropertyFlags::_flag_toggled() {
-	uint32_t value = 0;
-	for (int i = 0; i < flags.size(); i++) {
-		if (flags[i]->is_pressed()) {
-			uint32_t val = 1;
-			val <<= flag_indices[i];
-			value |= val;
-		}
+void EditorPropertyFlags::_flag_toggled(int p_index) {
+	uint32_t value = get_edited_object()->get(get_edited_property());
+	if (flags[p_index]->is_pressed()) {
+		value |= flag_values[p_index];
+	} else {
+		value &= ~flag_values[p_index];
 	}
 
 	emit_changed(get_edited_property(), value);
@@ -645,13 +643,7 @@ void EditorPropertyFlags::update_property() {
 	uint32_t value = get_edited_object()->get(get_edited_property());
 
 	for (int i = 0; i < flags.size(); i++) {
-		uint32_t val = 1;
-		val <<= flag_indices[i];
-		if (value & val) {
-			flags[i]->set_pressed(true);
-		} else {
-			flags[i]->set_pressed(false);
-		}
+		flags[i]->set_pressed((value & flag_values[i]) == flag_values[i]);
 	}
 }
 
@@ -659,17 +651,24 @@ void EditorPropertyFlags::setup(const Vector<String> &p_options) {
 	ERR_FAIL_COND(flags.size());
 
 	bool first = true;
+	uint32_t current_val;
 	for (int i = 0; i < p_options.size(); i++) {
 		String option = p_options[i].strip_edges();
 		if (option != "") {
 			CheckBox *cb = memnew(CheckBox);
 			cb->set_text(option);
 			cb->set_clip_text(true);
-			cb->connect("pressed", this, "_flag_toggled");
+			cb->connect("pressed", this, "_flag_toggled", varray(i));
 			add_focusable(cb);
 			vbox->add_child(cb);
 			flags.push_back(cb);
-			flag_indices.push_back(i);
+			Vector<String> text_split = p_options[i].split(":");
+			if (text_split.size() != 1) {
+				current_val = text_split[1].to_int();
+			} else {
+				current_val = 1 << i;
+			}
+			flag_values.push_back(current_val);
 			if (first) {
 				set_label_reference(cb);
 				first = false;

+ 2 - 2
editor/editor_properties.h

@@ -234,9 +234,9 @@ class EditorPropertyFlags : public EditorProperty {
 	GDCLASS(EditorPropertyFlags, EditorProperty);
 	VBoxContainer *vbox;
 	Vector<CheckBox *> flags;
-	Vector<int> flag_indices;
+	Vector<uint32_t> flag_values;
 
-	void _flag_toggled();
+	void _flag_toggled(int p_index);
 
 protected:
 	static void _bind_methods();

+ 19 - 14
editor/property_editor.cpp

@@ -114,15 +114,16 @@ void CustomPropertyEditor::_menu_option(int p_which) {
 	switch (type) {
 		case Variant::INT: {
 			if (hint == PROPERTY_HINT_FLAGS) {
-				int val = v;
-
-				if (val & (1 << p_which)) {
-					val &= ~(1 << p_which);
+				int idx = menu->get_item_index(p_which);
+				uint32_t item_value = menu->get_item_metadata(idx);
+				uint32_t value = v;
+				// If the item wasn't previously checked it means it was pressed,
+				// otherwise it was unpressed.
+				if (!menu->is_item_checked(idx)) {
+					v = value | item_value;
 				} else {
-					val |= (1 << p_which);
+					v = value & ~item_value;
 				}
-
-				v = val;
 				emit_signal("variant_changed");
 			} else if (hint == PROPERTY_HINT_ENUM) {
 				v = menu->get_item_metadata(p_which);
@@ -488,15 +489,19 @@ bool CustomPropertyEditor::edit(Object *p_owner, const String &p_name, Variant::
 				set_size(Size2(200, 150) * EDSCALE);
 			} else if (hint == PROPERTY_HINT_FLAGS) {
 				Vector<String> flags = hint_text.split(",");
+				uint32_t value = v;
 				for (int i = 0; i < flags.size(); i++) {
-					String flag = flags[i];
-					if (flag == "") {
-						continue;
+					uint32_t current_val;
+					Vector<String> text_split = flags[i].split(":");
+					if (text_split.size() != 1) {
+						current_val = text_split[1].to_int();
+					} else {
+						current_val = 1 << i;
 					}
-					menu->add_check_item(flag, i);
-					int f = v;
-					if (f & (1 << i)) {
-						menu->set_item_checked(menu->get_item_index(i), true);
+					menu->add_check_item(text_split[0], current_val);
+					menu->set_item_metadata(i, current_val);
+					if ((value & current_val) == current_val) {
+						menu->set_item_checked(menu->get_item_index(current_val), true);
 					}
 				}
 				menu->set_position(get_position());

+ 4 - 2
modules/mono/csharp_script.cpp

@@ -2618,7 +2618,8 @@ int CSharpScript::_try_get_member_export_hint(IMonoClassMember *p_member, Manage
 	GD_MONO_ASSERT_THREAD_ATTACHED;
 
 	if (p_variant_type == Variant::INT && p_type.type_encoding == MONO_TYPE_VALUETYPE && mono_class_is_enum(p_type.type_class->get_mono_ptr())) {
-		r_hint = PROPERTY_HINT_ENUM;
+		MonoReflectionType *reftype = mono_type_get_object(mono_domain_get(), p_type.type_class->get_mono_type());
+		r_hint = GDMonoUtils::Marshal::type_has_flags_attribute(reftype) ? PROPERTY_HINT_FLAGS : PROPERTY_HINT_ENUM;
 
 		Vector<MonoClassField *> fields = p_type.type_class->get_enum_fields();
 
@@ -2655,7 +2656,8 @@ int CSharpScript::_try_get_member_export_hint(IMonoClassMember *p_member, Manage
 			uint64_t val = GDMonoUtils::unbox_enum_value(val_obj, enum_basetype, r_error);
 			ERR_FAIL_COND_V_MSG(r_error, -1, "Failed to unbox '" + enum_field_name + "' constant enum value.");
 
-			if (val != (unsigned int)i) {
+			unsigned int expected_val = r_hint == PROPERTY_HINT_FLAGS ? 1 << i : i;
+			if (val != expected_val) {
 				uses_default_values = false;
 			}
 

+ 5 - 0
modules/mono/glue/GodotSharp/GodotSharp/Core/MarshalUtils.cs

@@ -79,6 +79,11 @@ namespace Godot
         /// </exception>
         private static bool TypeIsGenericIDictionary(Type type) => type.GetGenericTypeDefinition() == typeof(IDictionary<,>);
 
+        /// <summary>
+        /// Returns <see langword="true"/> if the <see cref="FlagsAttribute"/> is applied to the given type.
+        /// </summary>
+        private static bool TypeHasFlagsAttribute(Type type) => type.IsDefined(typeof(FlagsAttribute), false);
+
         /// <summary>
         /// Returns the generic type definition of <paramref name="type"/>.
         /// </summary>

+ 2 - 0
modules/mono/mono_gd/gd_mono_cache.cpp

@@ -166,6 +166,7 @@ void CachedData::clear_godot_api_cache() {
 	methodthunk_MarshalUtils_TypeIsGenericIEnumerable.nullify();
 	methodthunk_MarshalUtils_TypeIsGenericICollection.nullify();
 	methodthunk_MarshalUtils_TypeIsGenericIDictionary.nullify();
+	methodthunk_MarshalUtils_TypeHasFlagsAttribute.nullify();
 
 	methodthunk_MarshalUtils_GetGenericTypeDefinition.nullify();
 
@@ -280,6 +281,7 @@ void update_godot_api_cache() {
 	CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, TypeIsGenericIEnumerable, GODOT_API_CLASS(MarshalUtils)->get_method("TypeIsGenericIEnumerable", 1));
 	CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, TypeIsGenericICollection, GODOT_API_CLASS(MarshalUtils)->get_method("TypeIsGenericICollection", 1));
 	CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, TypeIsGenericIDictionary, GODOT_API_CLASS(MarshalUtils)->get_method("TypeIsGenericIDictionary", 1));
+	CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, TypeHasFlagsAttribute, GODOT_API_CLASS(MarshalUtils)->get_method("TypeHasFlagsAttribute", 1));
 
 	CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, GetGenericTypeDefinition, GODOT_API_CLASS(MarshalUtils)->get_method("GetGenericTypeDefinition", 2));
 

+ 1 - 0
modules/mono/mono_gd/gd_mono_cache.h

@@ -137,6 +137,7 @@ struct CachedData {
 	GDMonoMethodThunkR<MonoBoolean, MonoReflectionType *> methodthunk_MarshalUtils_TypeIsGenericIEnumerable;
 	GDMonoMethodThunkR<MonoBoolean, MonoReflectionType *> methodthunk_MarshalUtils_TypeIsGenericICollection;
 	GDMonoMethodThunkR<MonoBoolean, MonoReflectionType *> methodthunk_MarshalUtils_TypeIsGenericIDictionary;
+	GDMonoMethodThunkR<MonoBoolean, MonoReflectionType *> methodthunk_MarshalUtils_TypeHasFlagsAttribute;
 
 	GDMonoMethodThunk<MonoReflectionType *, MonoReflectionType **> methodthunk_MarshalUtils_GetGenericTypeDefinition;
 

+ 8 - 0
modules/mono/mono_gd/gd_mono_utils.cpp

@@ -587,6 +587,14 @@ bool type_is_generic_idictionary(MonoReflectionType *p_reftype) {
 	return (bool)res;
 }
 
+bool type_has_flags_attribute(MonoReflectionType *p_reftype) {
+	NO_GLUE_RET(false);
+	MonoException *exc = nullptr;
+	MonoBoolean res = CACHED_METHOD_THUNK(MarshalUtils, TypeHasFlagsAttribute).invoke(p_reftype, &exc);
+	UNHANDLED_EXCEPTION(exc);
+	return (bool)res;
+}
+
 void get_generic_type_definition(MonoReflectionType *p_reftype, MonoReflectionType **r_generic_reftype) {
 	MonoException *exc = nullptr;
 	CACHED_METHOD_THUNK(MarshalUtils, GetGenericTypeDefinition).invoke(p_reftype, r_generic_reftype, &exc);

+ 1 - 0
modules/mono/mono_gd/gd_mono_utils.h

@@ -61,6 +61,7 @@ bool type_is_system_generic_dictionary(MonoReflectionType *p_reftype);
 bool type_is_generic_ienumerable(MonoReflectionType *p_reftype);
 bool type_is_generic_icollection(MonoReflectionType *p_reftype);
 bool type_is_generic_idictionary(MonoReflectionType *p_reftype);
+bool type_has_flags_attribute(MonoReflectionType *p_reftype);
 
 void get_generic_type_definition(MonoReflectionType *p_reftype, MonoReflectionType **r_generic_reftype);
 

+ 43 - 1
modules/visual_script/visual_script_editor.cpp

@@ -869,7 +869,49 @@ void VisualScriptEditor::_update_graph(int p_only_id) {
 							EditorResourcePreview::get_singleton()->queue_edited_resource_preview(res, this, "_button_resource_previewed", arr);
 
 						} else if (pi.type == Variant::INT && pi.hint == PROPERTY_HINT_ENUM) {
-							button->set_text(pi.hint_string.get_slice(",", value));
+							bool found = false;
+							Vector<String> options = pi.hint_string.split(",");
+							int64_t current_val = 0;
+							for (int j = 0; j < options.size(); j++) {
+								const Vector<String> text_split = options[j].split(":");
+								if (text_split.size() != 1) {
+									current_val = text_split[1].to_int64();
+								}
+								if (value.operator int() == current_val) {
+									button->set_text(text_split[0]);
+									found = true;
+									break;
+								}
+								current_val += 1;
+							}
+							if (!found) {
+								button->set_text(value);
+							}
+						} else if (pi.type == Variant::INT && pi.hint == PROPERTY_HINT_FLAGS) {
+							Vector<String> value_texts;
+							const Vector<String> options = pi.hint_string.split(",");
+							uint32_t v = value;
+							for (int j = 0; j < options.size(); j++) {
+								uint32_t current_val;
+								Vector<String> text_split = options[j].split(":");
+								if (text_split.size() != -1) {
+									current_val = text_split[1].to_int();
+								} else {
+									current_val = 1 << i;
+								}
+								if ((v & current_val) == current_val) {
+									value_texts.push_back(text_split[0]);
+								}
+							}
+							if (value_texts.size() != 0) {
+								String value_text = value_texts[0];
+								for (int j = 1; j < value_texts.size(); j++) {
+									value_text += " | " + value_texts[j];
+								}
+								button->set_text(value_text);
+							} else {
+								button->set_text(value);
+							}
 						} else {
 							button->set_text(value);
 						}