Prechádzať zdrojové kódy

Implement a BitField hint

Allows to specify the binder that an enum must be treated as a bitfield.
reduz 3 rokov pred
rodič
commit
5ac42cf576

+ 9 - 9
core/core_bind.cpp

@@ -147,7 +147,7 @@ void ResourceLoader::_bind_methods() {
 
 ////// ResourceSaver //////
 
-Error ResourceSaver::save(const String &p_path, const Ref<Resource> &p_resource, uint32_t p_flags) {
+Error ResourceSaver::save(const String &p_path, const Ref<Resource> &p_resource, BitField<SaverFlags> p_flags) {
 	ERR_FAIL_COND_V_MSG(p_resource.is_null(), ERR_INVALID_PARAMETER, "Can't save empty resource to path '" + String(p_path) + "'.");
 	return ::ResourceSaver::save(p_path, p_resource, p_flags);
 }
@@ -179,14 +179,14 @@ void ResourceSaver::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("add_resource_format_saver", "format_saver", "at_front"), &ResourceSaver::add_resource_format_saver, DEFVAL(false));
 	ClassDB::bind_method(D_METHOD("remove_resource_format_saver", "format_saver"), &ResourceSaver::remove_resource_format_saver);
 
-	BIND_ENUM_CONSTANT(FLAG_NONE);
-	BIND_ENUM_CONSTANT(FLAG_RELATIVE_PATHS);
-	BIND_ENUM_CONSTANT(FLAG_BUNDLE_RESOURCES);
-	BIND_ENUM_CONSTANT(FLAG_CHANGE_PATH);
-	BIND_ENUM_CONSTANT(FLAG_OMIT_EDITOR_PROPERTIES);
-	BIND_ENUM_CONSTANT(FLAG_SAVE_BIG_ENDIAN);
-	BIND_ENUM_CONSTANT(FLAG_COMPRESS);
-	BIND_ENUM_CONSTANT(FLAG_REPLACE_SUBRESOURCE_PATHS);
+	BIND_BITFIELD_FLAG(FLAG_NONE);
+	BIND_BITFIELD_FLAG(FLAG_RELATIVE_PATHS);
+	BIND_BITFIELD_FLAG(FLAG_BUNDLE_RESOURCES);
+	BIND_BITFIELD_FLAG(FLAG_CHANGE_PATH);
+	BIND_BITFIELD_FLAG(FLAG_OMIT_EDITOR_PROPERTIES);
+	BIND_BITFIELD_FLAG(FLAG_SAVE_BIG_ENDIAN);
+	BIND_BITFIELD_FLAG(FLAG_COMPRESS);
+	BIND_BITFIELD_FLAG(FLAG_REPLACE_SUBRESOURCE_PATHS);
 }
 
 ////// OS //////

+ 2 - 2
core/core_bind.h

@@ -109,7 +109,7 @@ public:
 
 	static ResourceSaver *get_singleton() { return singleton; }
 
-	Error save(const String &p_path, const Ref<Resource> &p_resource, uint32_t p_flags);
+	Error save(const String &p_path, const Ref<Resource> &p_resource, BitField<SaverFlags> p_flags);
 	Vector<String> get_recognized_extensions(const Ref<Resource> &p_resource);
 	void add_resource_format_saver(Ref<ResourceFormatSaver> p_format_saver, bool p_at_front);
 	void remove_resource_format_saver(Ref<ResourceFormatSaver> p_format_saver);
@@ -719,7 +719,7 @@ public:
 VARIANT_ENUM_CAST(core_bind::ResourceLoader::ThreadLoadStatus);
 VARIANT_ENUM_CAST(core_bind::ResourceLoader::CacheMode);
 
-VARIANT_ENUM_CAST(core_bind::ResourceSaver::SaverFlags);
+VARIANT_BITFIELD_CAST(core_bind::ResourceSaver::SaverFlags);
 
 VARIANT_ENUM_CAST(core_bind::OS::VideoDriver);
 VARIANT_ENUM_CAST(core_bind::OS::Weekday);

+ 2 - 2
core/doc_data.cpp

@@ -38,7 +38,7 @@ void DocData::return_doc_from_retinfo(DocData::MethodDoc &p_method, const Proper
 		} else {
 			p_method.return_type += "*";
 		}
-	} else if (p_retinfo.type == Variant::INT && p_retinfo.usage & PROPERTY_USAGE_CLASS_IS_ENUM) {
+	} else if (p_retinfo.type == Variant::INT && p_retinfo.usage & (PROPERTY_USAGE_CLASS_IS_ENUM | PROPERTY_USAGE_CLASS_IS_BITFIELD)) {
 		p_method.return_enum = p_retinfo.class_name;
 		if (p_method.return_enum.begins_with("_")) { //proxy class
 			p_method.return_enum = p_method.return_enum.substr(1, p_method.return_enum.length());
@@ -69,7 +69,7 @@ void DocData::argument_doc_from_arginfo(DocData::ArgumentDoc &p_argument, const
 		} else {
 			p_argument.type += "*";
 		}
-	} else if (p_arginfo.type == Variant::INT && p_arginfo.usage & PROPERTY_USAGE_CLASS_IS_ENUM) {
+	} else if (p_arginfo.type == Variant::INT && p_arginfo.usage & (PROPERTY_USAGE_CLASS_IS_ENUM | PROPERTY_USAGE_CLASS_IS_BITFIELD)) {
 		p_argument.enumeration = p_arginfo.class_name;
 		if (p_argument.enumeration.begins_with("_")) { //proxy class
 			p_argument.enumeration = p_argument.enumeration.substr(1, p_argument.enumeration.length());

+ 2 - 0
core/doc_data.h

@@ -103,6 +103,7 @@ public:
 		String value;
 		bool is_value_valid = false;
 		String enumeration;
+		bool is_bitfield = false;
 		String description;
 		bool operator<(const ConstantDoc &p_const) const {
 			return name < p_const.name;
@@ -111,6 +112,7 @@ public:
 
 	struct EnumDoc {
 		String name = "@unnamed_enum";
+		bool is_bitfield = false;
 		String description;
 		Vector<DocData::ConstantDoc> values;
 	};

+ 2 - 1
core/extension/extension_api_dump.cpp

@@ -46,7 +46,7 @@ static String get_type_name(const PropertyInfo &p_info) {
 			return p_info.hint_string + "*";
 		}
 	}
-	if (p_info.type == Variant::INT && (p_info.usage & PROPERTY_USAGE_CLASS_IS_ENUM)) {
+	if (p_info.type == Variant::INT && (p_info.usage & (PROPERTY_USAGE_CLASS_IS_ENUM | PROPERTY_USAGE_CLASS_IS_BITFIELD))) {
 		return String("enum::") + String(p_info.class_name);
 	}
 	if (p_info.class_name != StringName()) {
@@ -665,6 +665,7 @@ Dictionary NativeExtensionAPIDump::generate_extension_api() {
 				for (const StringName &F : enum_list) {
 					Dictionary d2;
 					d2["name"] = String(F);
+					d2["is_bitfield"] = ClassDB::is_enum_bitfield(class_name, F);
 
 					Array values;
 					List<StringName> enum_constant_list;

+ 31 - 10
core/object/class_db.cpp

@@ -536,7 +536,7 @@ MethodBind *ClassDB::get_method(const StringName &p_class, const StringName &p_n
 	return nullptr;
 }
 
-void ClassDB::bind_integer_constant(const StringName &p_class, const StringName &p_enum, const StringName &p_name, int64_t p_constant) {
+void ClassDB::bind_integer_constant(const StringName &p_class, const StringName &p_enum, const StringName &p_name, int64_t p_constant, bool p_is_bitfield) {
 	OBJTYPE_WLOCK;
 
 	ClassInfo *type = classes.getptr(p_class);
@@ -555,13 +555,15 @@ void ClassDB::bind_integer_constant(const StringName &p_class, const StringName
 			enum_name = enum_name.get_slicec('.', 1);
 		}
 
-		List<StringName> *constants_list = type->enum_map.getptr(enum_name);
+		ClassInfo::EnumInfo *constants_list = type->enum_map.getptr(enum_name);
 
 		if (constants_list) {
-			constants_list->push_back(p_name);
+			constants_list->constants.push_back(p_name);
+			constants_list->is_bitfield = p_is_bitfield;
 		} else {
-			List<StringName> new_list;
-			new_list.push_back(p_name);
+			ClassInfo::EnumInfo new_list;
+			new_list.is_bitfield = p_is_bitfield;
+			new_list.constants.push_back(p_name);
 			type->enum_map[enum_name] = new_list;
 		}
 	}
@@ -645,8 +647,8 @@ StringName ClassDB::get_integer_constant_enum(const StringName &p_class, const S
 	ClassInfo *type = classes.getptr(p_class);
 
 	while (type) {
-		for (KeyValue<StringName, List<StringName>> &E : type->enum_map) {
-			List<StringName> &constants_list = E.value;
+		for (KeyValue<StringName, ClassInfo::EnumInfo> &E : type->enum_map) {
+			List<StringName> &constants_list = E.value.constants;
 			const List<StringName>::Element *found = constants_list.find(p_name);
 			if (found) {
 				return E.key;
@@ -669,7 +671,7 @@ void ClassDB::get_enum_list(const StringName &p_class, List<StringName> *p_enums
 	ClassInfo *type = classes.getptr(p_class);
 
 	while (type) {
-		for (KeyValue<StringName, List<StringName>> &E : type->enum_map) {
+		for (KeyValue<StringName, ClassInfo::EnumInfo> &E : type->enum_map) {
 			p_enums->push_back(E.key);
 		}
 
@@ -687,10 +689,10 @@ void ClassDB::get_enum_constants(const StringName &p_class, const StringName &p_
 	ClassInfo *type = classes.getptr(p_class);
 
 	while (type) {
-		const List<StringName> *constants = type->enum_map.getptr(p_enum);
+		const ClassInfo::EnumInfo *constants = type->enum_map.getptr(p_enum);
 
 		if (constants) {
-			for (const List<StringName>::Element *E = constants->front(); E; E = E->next()) {
+			for (const List<StringName>::Element *E = constants->constants.front(); E; E = E->next()) {
 				p_constants->push_back(E->get());
 			}
 		}
@@ -748,6 +750,25 @@ bool ClassDB::has_enum(const StringName &p_class, const StringName &p_name, bool
 	return false;
 }
 
+bool ClassDB::is_enum_bitfield(const StringName &p_class, const StringName &p_name, bool p_no_inheritance) {
+	OBJTYPE_RLOCK;
+
+	ClassInfo *type = classes.getptr(p_class);
+
+	while (type) {
+		if (type->enum_map.has(p_name) && type->enum_map[p_name].is_bitfield) {
+			return true;
+		}
+		if (p_no_inheritance) {
+			return false;
+		}
+
+		type = type->inherits_ptr;
+	}
+
+	return false;
+}
+
 void ClassDB::add_signal(const StringName &p_class, const MethodInfo &p_signal) {
 	OBJTYPE_WLOCK;
 

+ 15 - 2
core/object/class_db.h

@@ -104,7 +104,12 @@ public:
 
 		HashMap<StringName, MethodBind *> method_map;
 		HashMap<StringName, int64_t> constant_map;
-		HashMap<StringName, List<StringName>> enum_map;
+		struct EnumInfo {
+			List<StringName> constants;
+			bool is_bitfield = false;
+		};
+
+		HashMap<StringName, EnumInfo> enum_map;
 		HashMap<StringName, MethodInfo> signal_map;
 		List<PropertyInfo> property_list;
 		HashMap<StringName, PropertyInfo> property_map;
@@ -325,15 +330,17 @@ public:
 	static void add_virtual_method(const StringName &p_class, const MethodInfo &p_method, bool p_virtual = true, const Vector<String> &p_arg_names = Vector<String>(), bool p_object_core = false);
 	static void get_virtual_methods(const StringName &p_class, List<MethodInfo> *p_methods, bool p_no_inheritance = false);
 
-	static void bind_integer_constant(const StringName &p_class, const StringName &p_enum, const StringName &p_name, int64_t p_constant);
+	static void bind_integer_constant(const StringName &p_class, const StringName &p_enum, const StringName &p_name, int64_t p_constant, bool p_is_bitfield = false);
 	static void get_integer_constant_list(const StringName &p_class, List<String> *p_constants, bool p_no_inheritance = false);
 	static int64_t get_integer_constant(const StringName &p_class, const StringName &p_name, bool *p_success = nullptr);
 	static bool has_integer_constant(const StringName &p_class, const StringName &p_name, bool p_no_inheritance = false);
 
 	static StringName get_integer_constant_enum(const StringName &p_class, const StringName &p_name, bool p_no_inheritance = false);
+	static StringName get_integer_constant_bitfield(const StringName &p_class, const StringName &p_name, bool p_no_inheritance = false);
 	static void get_enum_list(const StringName &p_class, List<StringName> *p_enums, bool p_no_inheritance = false);
 	static void get_enum_constants(const StringName &p_class, const StringName &p_enum, List<StringName> *p_constants, bool p_no_inheritance = false);
 	static bool has_enum(const StringName &p_class, const StringName &p_name, bool p_no_inheritance = false);
+	static bool is_enum_bitfield(const StringName &p_class, const StringName &p_name, bool p_no_inheritance = false);
 
 	static void set_method_error_return_values(const StringName &p_class, const StringName &p_method, const Vector<Error> &p_values);
 	static Vector<Error> get_method_error_return_values(const StringName &p_class, const StringName &p_method);
@@ -370,6 +377,9 @@ public:
 #define BIND_ENUM_CONSTANT(m_constant) \
 	::ClassDB::bind_integer_constant(get_class_static(), __constant_get_enum_name(m_constant, #m_constant), #m_constant, m_constant);
 
+#define BIND_BITFIELD_FLAG(m_constant) \
+	::ClassDB::bind_integer_constant(get_class_static(), __constant_get_bitfield_name(m_constant, #m_constant), #m_constant, m_constant, true);
+
 _FORCE_INLINE_ void errarray_add_str(Vector<Error> &arr) {
 }
 
@@ -401,6 +411,9 @@ _FORCE_INLINE_ Vector<Error> errarray(P... p_args) {
 #define BIND_ENUM_CONSTANT(m_constant) \
 	::ClassDB::bind_integer_constant(get_class_static(), StringName(), #m_constant, m_constant);
 
+#define BIND_BITFIELD_FLAG(m_constant) \
+	::ClassDB::bind_integer_constant(get_class_static(), StringName(), #m_constant, m_constant, true);
+
 #define BIND_METHOD_ERR_RETURN_DOC(m_method, ...)
 
 #endif

+ 1 - 0
core/object/object.h

@@ -109,6 +109,7 @@ enum PropertyUsageFlags {
 	PROPERTY_USAGE_GROUP = 128, //used for grouping props in the editor
 	PROPERTY_USAGE_CATEGORY = 256,
 	PROPERTY_USAGE_SUBGROUP = 512,
+	PROPERTY_USAGE_CLASS_IS_BITFIELD = 1024,
 	PROPERTY_USAGE_NO_INSTANCE_STATE = 2048,
 	PROPERTY_USAGE_RESTART_IF_CHANGED = 4096,
 	PROPERTY_USAGE_SCRIPT_VARIABLE = 8192,

+ 23 - 0
core/variant/binder_common.h

@@ -106,6 +106,29 @@ struct VariantCaster<const T &> {
 		static void initialize(m_enum &value) { value = (m_enum)0; }         \
 	};
 
+#define VARIANT_BITFIELD_CAST(m_enum)                                                  \
+	MAKE_BITFIELD_TYPE_INFO(m_enum)                                                    \
+	template <>                                                                        \
+	struct VariantCaster<BitField<m_enum>> {                                           \
+		static _FORCE_INLINE_ BitField<m_enum> cast(const Variant &p_variant) {        \
+			return BitField<m_enum>(p_variant.operator int64_t());                     \
+		}                                                                              \
+	};                                                                                 \
+	template <>                                                                        \
+	struct PtrToArg<BitField<m_enum>> {                                                \
+		_FORCE_INLINE_ static BitField<m_enum> convert(const void *p_ptr) {            \
+			return BitField<m_enum>(*reinterpret_cast<const int64_t *>(p_ptr));        \
+		}                                                                              \
+		typedef int64_t EncodeT;                                                       \
+		_FORCE_INLINE_ static void encode(BitField<m_enum> p_val, const void *p_ptr) { \
+			*(int64_t *)p_ptr = p_val;                                                 \
+		}                                                                              \
+	};                                                                                 \
+	template <>                                                                        \
+	struct ZeroInitializer<BitField<m_enum>> {                                         \
+		static void initialize(BitField<m_enum> &value) { value = 0; }                 \
+	};
+
 // Object enum casts must go here
 VARIANT_ENUM_CAST(Object::ConnectFlags);
 

+ 45 - 0
core/variant/type_info.h

@@ -279,6 +279,51 @@ inline StringName __constant_get_enum_name(T param, const String &p_constant) {
 	return GetTypeInfo<T>::get_class_info().class_name;
 }
 
+template <class T>
+class BitField {
+	uint32_t value = 0;
+
+public:
+	_FORCE_INLINE_ void set_flag(T p_flag) { value |= p_flag; }
+	_FORCE_INLINE_ bool has_flag(T p_flag) const { return value & p_flag; }
+	_FORCE_INLINE_ void clear_flag(T p_flag) { return value &= ~p_flag; }
+	_FORCE_INLINE_ BitField(uint32_t p_value) { value = p_value; }
+	_FORCE_INLINE_ operator uint32_t() const { return value; }
+};
+
+#define TEMPL_MAKE_BITFIELD_TYPE_INFO(m_enum, m_impl)                                                                                            \
+	template <>                                                                                                                                  \
+	struct GetTypeInfo<m_impl> {                                                                                                                 \
+		static const Variant::Type VARIANT_TYPE = Variant::INT;                                                                                  \
+		static const GodotTypeInfo::Metadata METADATA = GodotTypeInfo::METADATA_NONE;                                                            \
+		static inline PropertyInfo get_class_info() {                                                                                            \
+			return PropertyInfo(Variant::INT, String(), PROPERTY_HINT_NONE, String(), PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_CLASS_IS_BITFIELD, \
+					godot::details::enum_qualified_name_to_class_info_name(String(#m_enum)));                                                    \
+		}                                                                                                                                        \
+	};                                                                                                                                           \
+	template <>                                                                                                                                  \
+	struct GetTypeInfo<BitField<m_impl>> {                                                                                                       \
+		static const Variant::Type VARIANT_TYPE = Variant::INT;                                                                                  \
+		static const GodotTypeInfo::Metadata METADATA = GodotTypeInfo::METADATA_NONE;                                                            \
+		static inline PropertyInfo get_class_info() {                                                                                            \
+			return PropertyInfo(Variant::INT, String(), PROPERTY_HINT_NONE, String(), PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_CLASS_IS_BITFIELD, \
+					godot::details::enum_qualified_name_to_class_info_name(String(#m_enum)));                                                    \
+		}                                                                                                                                        \
+	};
+
+#define MAKE_BITFIELD_TYPE_INFO(m_enum)                 \
+	TEMPL_MAKE_BITFIELD_TYPE_INFO(m_enum, m_enum)       \
+	TEMPL_MAKE_BITFIELD_TYPE_INFO(m_enum, m_enum const) \
+	TEMPL_MAKE_BITFIELD_TYPE_INFO(m_enum, m_enum &)     \
+	TEMPL_MAKE_BITFIELD_TYPE_INFO(m_enum, const m_enum &)
+
+template <typename T>
+inline StringName __constant_get_bitfield_name(T param, const String &p_constant) {
+	if (GetTypeInfo<T>::VARIANT_TYPE == Variant::NIL) {
+		ERR_PRINT("Missing VARIANT_ENUM_CAST for constant's bitfield: " + p_constant);
+	}
+	return GetTypeInfo<BitField<T>>::get_class_info().class_name;
+}
 #define CLASS_INFO(m_type) (GetTypeInfo<m_type *>::get_class_info())
 
 template <typename T>

+ 1 - 0
doc/class.xsd

@@ -155,6 +155,7 @@
 											<xs:attribute type="xs:string" name="name" />
 											<xs:attribute type="xs:string" name="value" />
 											<xs:attribute type="xs:string" name="enum" use="optional" />
+											<xs:attribute type="xs:boolean" name="is_bitfield" use="optional" />
 										</xs:extension>
 									</xs:simpleContent>
 								</xs:complexType>

+ 9 - 9
doc/classes/ResourceSaver.xml

@@ -37,7 +37,7 @@
 			<return type="int" enum="Error" />
 			<argument index="0" name="path" type="String" />
 			<argument index="1" name="resource" type="Resource" />
-			<argument index="2" name="flags" type="int" default="0" />
+			<argument index="2" name="flags" type="int" enum="ResourceSaver.SaverFlags" default="0" />
 			<description>
 				Saves a resource to disk to the given path, using a [ResourceFormatSaver] that recognizes the resource object.
 				The [code]flags[/code] bitmask can be specified to customize the save behavior using [enum SaverFlags] flags.
@@ -46,28 +46,28 @@
 		</method>
 	</methods>
 	<constants>
-		<constant name="FLAG_NONE" value="0" enum="SaverFlags">
+		<constant name="FLAG_NONE" value="0" enum="SaverFlags" is_bitfield="true">
 			No resource saving option.
 		</constant>
-		<constant name="FLAG_RELATIVE_PATHS" value="1" enum="SaverFlags">
+		<constant name="FLAG_RELATIVE_PATHS" value="1" enum="SaverFlags" is_bitfield="true">
 			Save the resource with a path relative to the scene which uses it.
 		</constant>
-		<constant name="FLAG_BUNDLE_RESOURCES" value="2" enum="SaverFlags">
+		<constant name="FLAG_BUNDLE_RESOURCES" value="2" enum="SaverFlags" is_bitfield="true">
 			Bundles external resources.
 		</constant>
-		<constant name="FLAG_CHANGE_PATH" value="4" enum="SaverFlags">
+		<constant name="FLAG_CHANGE_PATH" value="4" enum="SaverFlags" is_bitfield="true">
 			Changes the [member Resource.resource_path] of the saved resource to match its new location.
 		</constant>
-		<constant name="FLAG_OMIT_EDITOR_PROPERTIES" value="8" enum="SaverFlags">
+		<constant name="FLAG_OMIT_EDITOR_PROPERTIES" value="8" enum="SaverFlags" is_bitfield="true">
 			Do not save editor-specific metadata (identified by their [code]__editor[/code] prefix).
 		</constant>
-		<constant name="FLAG_SAVE_BIG_ENDIAN" value="16" enum="SaverFlags">
+		<constant name="FLAG_SAVE_BIG_ENDIAN" value="16" enum="SaverFlags" is_bitfield="true">
 			Save as big endian (see [member File.big_endian]).
 		</constant>
-		<constant name="FLAG_COMPRESS" value="32" enum="SaverFlags">
+		<constant name="FLAG_COMPRESS" value="32" enum="SaverFlags" is_bitfield="true">
 			Compress the resource on save using [constant File.COMPRESSION_ZSTD]. Only available for binary resource types.
 		</constant>
-		<constant name="FLAG_REPLACE_SUBRESOURCE_PATHS" value="64" enum="SaverFlags">
+		<constant name="FLAG_REPLACE_SUBRESOURCE_PATHS" value="64" enum="SaverFlags" is_bitfield="true">
 			Take over the paths of the saved subresources (see [method Resource.take_over_path]).
 		</constant>
 	</constants>

+ 12 - 5
doc/tools/make_rst.py

@@ -122,16 +122,18 @@ class MethodDef:
 
 
 class ConstantDef:
-    def __init__(self, name, value, text):  # type: (str, str, Optional[str]) -> None
+    def __init__(self, name, value, text, bitfield):  # type: (str, str, Optional[str], Optional[bool]) -> None
         self.name = name
         self.value = value
         self.text = text
+        self.is_bitfield = bitfield
 
 
 class EnumDef:
-    def __init__(self, name):  # type: (str) -> None
+    def __init__(self, name, bitfield):  # type: (str, Optional[bool]) -> None
         self.name = name
         self.values = OrderedDict()  # type: OrderedDict[str, ConstantDef]
+        self.is_bitfield = bitfield
 
 
 class ThemeItemDef:
@@ -305,7 +307,8 @@ class State:
                 constant_name = constant.attrib["name"]
                 value = constant.attrib["value"]
                 enum = constant.get("enum")
-                constant_def = ConstantDef(constant_name, value, constant.text)
+                is_bitfield = constant.get("is_bitfield") or False
+                constant_def = ConstantDef(constant_name, value, constant.text, is_bitfield)
                 if enum is None:
                     if constant_name in class_def.constants:
                         print_error('{}.xml: Duplicate constant "{}".'.format(class_name, constant_name), self)
@@ -318,7 +321,7 @@ class State:
                         enum_def = class_def.enums[enum]
 
                     else:
-                        enum_def = EnumDef(enum)
+                        enum_def = EnumDef(enum, is_bitfield)
                         class_def.enums[enum] = enum_def
 
                     enum_def.values[constant_name] = constant_def
@@ -706,7 +709,11 @@ def make_rst_class(class_def, state, dry_run, output_dir):  # type: (ClassDef, S
             for value in e.values.values():
                 f.write(".. _class_{}_constant_{}:\n\n".format(class_name, value.name))
 
-            f.write("enum **{}**:\n\n".format(e.name))
+            if e.is_bitfield:
+                f.write("flags **{}**:\n\n".format(e.name))
+            else:
+                f.write("enum **{}**:\n\n".format(e.name))
+
             for value in e.values.values():
                 f.write("- **{}** = **{}**".format(value.name, value.value))
                 if value.text is not None and value.text.strip() != "":

+ 10 - 2
editor/doc_tools.cpp

@@ -429,7 +429,7 @@ void DocTools::generate(bool p_basic_types) {
 					PropertyInfo retinfo = mb->get_return_info();
 
 					found_type = true;
-					if (retinfo.type == Variant::INT && retinfo.usage & PROPERTY_USAGE_CLASS_IS_ENUM) {
+					if (retinfo.type == Variant::INT && retinfo.usage & (PROPERTY_USAGE_CLASS_IS_ENUM | PROPERTY_USAGE_CLASS_IS_BITFIELD)) {
 						prop.enumeration = retinfo.class_name;
 						prop.type = "int";
 					} else if (retinfo.class_name != StringName()) {
@@ -575,6 +575,7 @@ void DocTools::generate(bool p_basic_types) {
 			constant.value = itos(ClassDB::get_integer_constant(name, E));
 			constant.is_value_valid = true;
 			constant.enumeration = ClassDB::get_integer_constant_enum(name, E);
+			constant.is_bitfield = ClassDB::is_enum_bitfield(name, constant.enumeration);
 			c.constants.push_back(constant);
 		}
 
@@ -1244,6 +1245,9 @@ Error DocTools::_load(Ref<XMLParser> parser) {
 								if (parser->has_attribute("enum")) {
 									constant2.enumeration = parser->get_attribute_value("enum");
 								}
+								if (parser->has_attribute("is_bitfield")) {
+									constant2.is_bitfield = parser->get_attribute_value("is_bitfield").to_lower() == "true";
+								}
 								if (!parser->is_empty()) {
 									parser->read();
 									if (parser->get_node_type() == XMLParser::NODE_TEXT) {
@@ -1424,7 +1428,11 @@ Error DocTools::save_classes(const String &p_default_path, const HashMap<String,
 				const DocData::ConstantDoc &k = c.constants[i];
 				if (k.is_value_valid) {
 					if (!k.enumeration.is_empty()) {
-						_write_string(f, 2, "<constant name=\"" + k.name + "\" value=\"" + k.value + "\" enum=\"" + k.enumeration + "\">");
+						if (k.is_bitfield) {
+							_write_string(f, 2, "<constant name=\"" + k.name + "\" value=\"" + k.value + "\" enum=\"" + k.enumeration + "\" is_bitfield=\"true\">");
+						} else {
+							_write_string(f, 2, "<constant name=\"" + k.name + "\" value=\"" + k.value + "\" enum=\"" + k.enumeration + "\">");
+						}
 					} else {
 						_write_string(f, 2, "<constant name=\"" + k.name + "\" value=\"" + k.value + "\">");
 					}

+ 5 - 1
editor/editor_help.cpp

@@ -1105,7 +1105,11 @@ void EditorHelp::_update_doc() {
 
 				class_desc->push_font(doc_code_font);
 				class_desc->push_color(title_color);
-				class_desc->add_text("enum  ");
+				if (E.value.size() && E.value[0].is_bitfield) {
+					class_desc->add_text("flags  ");
+				} else {
+					class_desc->add_text("enum  ");
+				}
 				class_desc->pop();
 				String e = E.key;
 				if ((e.get_slice_count(".") > 1) && (e.get_slice(".", 0) == edited_class)) {

+ 3 - 3
modules/gdscript/gdscript_editor.cpp

@@ -569,7 +569,7 @@ static int _get_enum_constant_location(StringName p_class, StringName p_enum_con
 // END LOCATION METHODS
 
 static String _get_visual_datatype(const PropertyInfo &p_info, bool p_is_arg = true) {
-	if (p_info.usage & PROPERTY_USAGE_CLASS_IS_ENUM) {
+	if (p_info.usage & (PROPERTY_USAGE_CLASS_IS_ENUM | PROPERTY_USAGE_CLASS_IS_BITFIELD)) {
 		String enum_name = p_info.class_name;
 		if (!enum_name.contains(".")) {
 			return enum_name;
@@ -1292,7 +1292,7 @@ static GDScriptCompletionIdentifier _type_from_property(const PropertyInfo &p_pr
 		return ci;
 	}
 
-	if (p_property.usage & PROPERTY_USAGE_CLASS_IS_ENUM) {
+	if (p_property.usage & (PROPERTY_USAGE_CLASS_IS_ENUM | PROPERTY_USAGE_CLASS_IS_BITFIELD)) {
 		ci.enumeration = p_property.class_name;
 	}
 
@@ -2407,7 +2407,7 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c
 
 					if (p_argidx < method_args) {
 						PropertyInfo arg_info = info.arguments[p_argidx];
-						if (arg_info.usage & PROPERTY_USAGE_CLASS_IS_ENUM) {
+						if (arg_info.usage & (PROPERTY_USAGE_CLASS_IS_ENUM | PROPERTY_USAGE_CLASS_IS_BITFIELD)) {
 							_find_enumeration_candidates(p_context, arg_info.class_name, r_result);
 						}
 					}

+ 6 - 6
modules/mono/editor/bindings_generator.cpp

@@ -2865,7 +2865,7 @@ bool BindingsGenerator::_populate_object_type_interfaces() {
 							   " We only expected Object.free, but found '" +
 							itype.name + "." + imethod.name + "'.");
 				}
-			} else if (return_info.type == Variant::INT && return_info.usage & PROPERTY_USAGE_CLASS_IS_ENUM) {
+			} else if (return_info.type == Variant::INT && return_info.usage & (PROPERTY_USAGE_CLASS_IS_ENUM | PROPERTY_USAGE_CLASS_IS_BITFIELD)) {
 				imethod.return_type.cname = return_info.class_name;
 				imethod.return_type.is_enum = true;
 			} else if (return_info.class_name != StringName()) {
@@ -2903,7 +2903,7 @@ bool BindingsGenerator::_populate_object_type_interfaces() {
 				ArgumentInterface iarg;
 				iarg.name = orig_arg_name;
 
-				if (arginfo.type == Variant::INT && arginfo.usage & PROPERTY_USAGE_CLASS_IS_ENUM) {
+				if (arginfo.type == Variant::INT && arginfo.usage & (PROPERTY_USAGE_CLASS_IS_ENUM | PROPERTY_USAGE_CLASS_IS_BITFIELD)) {
 					iarg.type.cname = arginfo.class_name;
 					iarg.type.is_enum = true;
 				} else if (arginfo.class_name != StringName()) {
@@ -3011,7 +3011,7 @@ bool BindingsGenerator::_populate_object_type_interfaces() {
 				ArgumentInterface iarg;
 				iarg.name = orig_arg_name;
 
-				if (arginfo.type == Variant::INT && arginfo.usage & PROPERTY_USAGE_CLASS_IS_ENUM) {
+				if (arginfo.type == Variant::INT && arginfo.usage & (PROPERTY_USAGE_CLASS_IS_ENUM | PROPERTY_USAGE_CLASS_IS_BITFIELD)) {
 					iarg.type.cname = arginfo.class_name;
 					iarg.type.is_enum = true;
 				} else if (arginfo.class_name != StringName()) {
@@ -3075,9 +3075,9 @@ bool BindingsGenerator::_populate_object_type_interfaces() {
 		List<String> constants;
 		ClassDB::get_integer_constant_list(type_cname, &constants, true);
 
-		const HashMap<StringName, List<StringName>> &enum_map = class_info->enum_map;
+		const HashMap<StringName, ClassDB::ClassInfo::EnumInfo> &enum_map = class_info->enum_map;
 
-		for (const KeyValue<StringName, List<StringName>> &E : enum_map) {
+		for (const KeyValue<StringName, ClassDB::ClassInfo::EnumInfo> &E : enum_map) {
 			StringName enum_proxy_cname = E.key;
 			String enum_proxy_name = enum_proxy_cname.operator String();
 			if (itype.find_property_by_proxy_name(enum_proxy_cname)) {
@@ -3087,7 +3087,7 @@ bool BindingsGenerator::_populate_object_type_interfaces() {
 				enum_proxy_cname = StringName(enum_proxy_name);
 			}
 			EnumInterface ienum(enum_proxy_cname);
-			const List<StringName> &enum_constants = E.value;
+			const List<StringName> &enum_constants = E.value.constants;
 			for (const StringName &constant_cname : enum_constants) {
 				String constant_name = constant_cname.operator String();
 				int64_t *value = class_info->constant_map.getptr(constant_cname);

+ 6 - 6
tests/core/object/test_class_db.h

@@ -597,7 +597,7 @@ void add_exposed_classes(Context &r_context) {
 						(exposed_class.name != r_context.names_cache.object_class || String(method.name) != "free"),
 						warn_msg.utf8().get_data());
 
-			} else if (return_info.type == Variant::INT && return_info.usage & PROPERTY_USAGE_CLASS_IS_ENUM) {
+			} else if (return_info.type == Variant::INT && return_info.usage & (PROPERTY_USAGE_CLASS_IS_ENUM | PROPERTY_USAGE_CLASS_IS_BITFIELD)) {
 				method.return_type.name = return_info.class_name;
 				method.return_type.is_enum = true;
 			} else if (return_info.class_name != StringName()) {
@@ -626,7 +626,7 @@ void add_exposed_classes(Context &r_context) {
 				ArgumentData arg;
 				arg.name = orig_arg_name;
 
-				if (arg_info.type == Variant::INT && arg_info.usage & PROPERTY_USAGE_CLASS_IS_ENUM) {
+				if (arg_info.type == Variant::INT && arg_info.usage & (PROPERTY_USAGE_CLASS_IS_ENUM | PROPERTY_USAGE_CLASS_IS_BITFIELD)) {
 					arg.type.name = arg_info.class_name;
 					arg.type.is_enum = true;
 				} else if (arg_info.class_name != StringName()) {
@@ -694,7 +694,7 @@ void add_exposed_classes(Context &r_context) {
 				ArgumentData arg;
 				arg.name = orig_arg_name;
 
-				if (arg_info.type == Variant::INT && arg_info.usage & PROPERTY_USAGE_CLASS_IS_ENUM) {
+				if (arg_info.type == Variant::INT && arg_info.usage & (PROPERTY_USAGE_CLASS_IS_ENUM | PROPERTY_USAGE_CLASS_IS_BITFIELD)) {
 					arg.type.name = arg_info.class_name;
 					arg.type.is_enum = true;
 				} else if (arg_info.class_name != StringName()) {
@@ -732,13 +732,13 @@ void add_exposed_classes(Context &r_context) {
 		List<String> constants;
 		ClassDB::get_integer_constant_list(class_name, &constants, true);
 
-		const HashMap<StringName, List<StringName>> &enum_map = class_info->enum_map;
+		const HashMap<StringName, ClassDB::ClassInfo::EnumInfo> &enum_map = class_info->enum_map;
 
-		for (const KeyValue<StringName, List<StringName>> &K : enum_map) {
+		for (const KeyValue<StringName, ClassDB::ClassInfo::EnumInfo> &K : enum_map) {
 			EnumData enum_;
 			enum_.name = K.key;
 
-			for (const StringName &E : K.value) {
+			for (const StringName &E : K.value.constants) {
 				const StringName &constant_name = E;
 				TEST_FAIL_COND(String(constant_name).find("::") != -1,
 						"Enum constant contains '::', check bindings to remove the scope: '",