diff --git a/gdextension/gdextension_interface.h b/gdextension/gdextension_interface.h index ba0503d1..9d9ae20c 100644 --- a/gdextension/gdextension_interface.h +++ b/gdextension/gdextension_interface.h @@ -258,6 +258,7 @@ typedef const GDExtensionPropertyInfo *(*GDExtensionClassGetPropertyList)(GDExte typedef void (*GDExtensionClassFreePropertyList)(GDExtensionClassInstancePtr p_instance, const GDExtensionPropertyInfo *p_list); typedef GDExtensionBool (*GDExtensionClassPropertyCanRevert)(GDExtensionClassInstancePtr p_instance, GDExtensionConstStringNamePtr p_name); typedef GDExtensionBool (*GDExtensionClassPropertyGetRevert)(GDExtensionClassInstancePtr p_instance, GDExtensionConstStringNamePtr p_name, GDExtensionVariantPtr r_ret); +typedef GDExtensionBool (*GDExtensionClassValidateProperty)(GDExtensionClassInstancePtr p_instance, GDExtensionPropertyInfo *p_property); typedef void (*GDExtensionClassNotification)(GDExtensionClassInstancePtr p_instance, int32_t p_what); // Deprecated. Use GDExtensionClassNotification2 instead. typedef void (*GDExtensionClassNotification2)(GDExtensionClassInstancePtr p_instance, int32_t p_what, GDExtensionBool p_reversed); typedef void (*GDExtensionClassToString)(GDExtensionClassInstancePtr p_instance, GDExtensionBool *r_is_valid, GDExtensionStringPtr p_out); @@ -298,6 +299,7 @@ typedef struct { GDExtensionClassFreePropertyList free_property_list_func; GDExtensionClassPropertyCanRevert property_can_revert_func; GDExtensionClassPropertyGetRevert property_get_revert_func; + GDExtensionClassValidateProperty validate_property_func; GDExtensionClassNotification2 notification_func; GDExtensionClassToString to_string_func; GDExtensionClassReference reference_func; @@ -374,6 +376,7 @@ typedef GDExtensionBool (*GDExtensionScriptInstanceGet)(GDExtensionScriptInstanc typedef const GDExtensionPropertyInfo *(*GDExtensionScriptInstanceGetPropertyList)(GDExtensionScriptInstanceDataPtr p_instance, uint32_t *r_count); typedef void (*GDExtensionScriptInstanceFreePropertyList)(GDExtensionScriptInstanceDataPtr p_instance, const GDExtensionPropertyInfo *p_list); typedef GDExtensionVariantType (*GDExtensionScriptInstanceGetPropertyType)(GDExtensionScriptInstanceDataPtr p_instance, GDExtensionConstStringNamePtr p_name, GDExtensionBool *r_is_valid); +typedef GDExtensionBool (*GDExtensionScriptInstanceValidateProperty)(GDExtensionScriptInstanceDataPtr p_instance, GDExtensionPropertyInfo *p_property); typedef GDExtensionBool (*GDExtensionScriptInstancePropertyCanRevert)(GDExtensionScriptInstanceDataPtr p_instance, GDExtensionConstStringNamePtr p_name); typedef GDExtensionBool (*GDExtensionScriptInstancePropertyGetRevert)(GDExtensionScriptInstanceDataPtr p_instance, GDExtensionConstStringNamePtr p_name, GDExtensionVariantPtr r_ret); @@ -460,6 +463,7 @@ typedef struct { GDExtensionScriptInstanceGetMethodList get_method_list_func; GDExtensionScriptInstanceFreeMethodList free_method_list_func; GDExtensionScriptInstanceGetPropertyType get_property_type_func; + GDExtensionScriptInstanceValidateProperty validate_property_func; GDExtensionScriptInstanceHasMethod has_method_func; diff --git a/include/godot_cpp/classes/wrapped.hpp b/include/godot_cpp/classes/wrapped.hpp index aa2c9fc4..c6e452cf 100644 --- a/include/godot_cpp/classes/wrapped.hpp +++ b/include/godot_cpp/classes/wrapped.hpp @@ -60,6 +60,7 @@ protected: void _get_property_list(List *p_list) const {} bool _property_can_revert(const StringName &p_name) const { return false; } bool _property_get_revert(const StringName &p_name, Variant &r_property) const { return false; } + void _validate_property(PropertyInfo &p_property) const {} String _to_string() const { return "[" + String(get_class_static()) + ":" + itos(get_instance_id()) + "]"; } static void notification_bind(GDExtensionClassInstancePtr p_instance, int32_t p_what, GDExtensionBool p_reversed) {} @@ -69,6 +70,7 @@ protected: static void free_property_list_bind(GDExtensionClassInstancePtr p_instance, const GDExtensionPropertyInfo *p_list) {} static GDExtensionBool property_can_revert_bind(GDExtensionClassInstancePtr p_instance, GDExtensionConstStringNamePtr p_name) { return false; } static GDExtensionBool property_get_revert_bind(GDExtensionClassInstancePtr p_instance, GDExtensionConstStringNamePtr p_name, GDExtensionVariantPtr r_ret) { return false; } + static GDExtensionBool validate_property_bind(GDExtensionClassInstancePtr p_instance, GDExtensionPropertyInfo *p_property) { return false; } static void to_string_bind(GDExtensionClassInstancePtr p_instance, GDExtensionBool *r_is_valid, GDExtensionStringPtr r_out) {} // The only reason this has to be held here, is when we return results of `_get_property_list` to Godot, we pass @@ -150,6 +152,10 @@ protected: return (bool(::godot::Wrapped::*)(const ::godot::StringName &p_name, ::godot::Variant &) const) & m_class::_property_get_revert; \ } \ \ + static void (::godot::Wrapped::*_get_validate_property())(::godot::PropertyInfo & p_property) const { \ + return (void(::godot::Wrapped::*)(::godot::PropertyInfo & p_property) const) & m_class::_validate_property; \ + } \ + \ static ::godot::String (::godot::Wrapped::*_get_to_string())() const { \ return (::godot::String(::godot::Wrapped::*)() const) & m_class::_to_string; \ } \ @@ -267,6 +273,21 @@ public: return false; \ } \ \ + static GDExtensionBool validate_property_bind(GDExtensionClassInstancePtr p_instance, GDExtensionPropertyInfo *p_property) { \ + bool ret = false; \ + if (p_instance && m_class::_get_validate_property()) { \ + ret = m_inherits::validate_property_bind(p_instance, p_property); \ + if (m_class::_get_validate_property() != m_inherits::_get_validate_property()) { \ + m_class *cls = reinterpret_cast(p_instance); \ + ::godot::PropertyInfo info(p_property); \ + cls->_validate_property(info); \ + info._update(p_property); \ + return true; \ + } \ + } \ + return ret; \ + } \ + \ static void to_string_bind(GDExtensionClassInstancePtr p_instance, GDExtensionBool *r_is_valid, GDExtensionStringPtr r_out) { \ if (p_instance && m_class::_get_to_string()) { \ if (m_class::_get_to_string() != m_inherits::_get_to_string()) { \ @@ -345,6 +366,10 @@ protected: return nullptr; \ } \ \ + static void (Wrapped::*_get_validate_property())(::godot::PropertyInfo & p_property) const { \ + return nullptr; \ + } \ + \ static String (Wrapped::*_get_to_string())() const { \ return nullptr; \ } \ diff --git a/include/godot_cpp/core/class_db.hpp b/include/godot_cpp/core/class_db.hpp index 65694453..1a17b573 100644 --- a/include/godot_cpp/core/class_db.hpp +++ b/include/godot_cpp/core/class_db.hpp @@ -186,6 +186,7 @@ void ClassDB::_register_class(bool p_virtual, bool p_exposed) { T::free_property_list_bind, // GDExtensionClassFreePropertyList free_property_list_func; T::property_can_revert_bind, // GDExtensionClassPropertyCanRevert property_can_revert_func; T::property_get_revert_bind, // GDExtensionClassPropertyGetRevert property_get_revert_func; + T::validate_property_bind, // GDExtensionClassValidateProperty validate_property_func; T::notification_bind, // GDExtensionClassNotification2 notification_func; T::to_string_bind, // GDExtensionClassToString to_string_func; nullptr, // GDExtensionClassReference reference_func; diff --git a/include/godot_cpp/core/property_info.hpp b/include/godot_cpp/core/property_info.hpp index 8146859f..0ecfa322 100644 --- a/include/godot_cpp/core/property_info.hpp +++ b/include/godot_cpp/core/property_info.hpp @@ -68,6 +68,18 @@ struct PropertyInfo { PropertyInfo(GDExtensionVariantType p_type, const StringName &p_name, PropertyHint p_hint = PROPERTY_HINT_NONE, const String &p_hint_string = "", uint32_t p_usage = PROPERTY_USAGE_DEFAULT, const StringName &p_class_name = "") : PropertyInfo((Variant::Type)p_type, p_name, p_hint, p_hint_string, p_usage, p_class_name) {} + + PropertyInfo(const GDExtensionPropertyInfo *p_info) : + PropertyInfo(p_info->type, *reinterpret_cast(p_info->name), (PropertyHint)p_info->hint, *reinterpret_cast(p_info->hint_string), p_info->usage, *reinterpret_cast(p_info->class_name)) {} + + void _update(GDExtensionPropertyInfo *p_info) { + p_info->type = (GDExtensionVariantType)type; + *(reinterpret_cast(p_info->name)) = name; + p_info->hint = hint; + *(reinterpret_cast(p_info->hint_string)) = hint_string; + p_info->usage = usage; + *(reinterpret_cast(p_info->class_name)) = class_name; + } }; } // namespace godot diff --git a/test/project/main.gd b/test/project/main.gd index 473c15fe..715b13e8 100644 --- a/test/project/main.gd +++ b/test/project/main.gd @@ -23,6 +23,10 @@ func _ready(): # Property list. example.property_from_list = Vector3(100, 200, 300) assert_equal(example.property_from_list, Vector3(100, 200, 300)) + var prop_list = example.get_property_list() + for prop_info in prop_list: + if prop_info['name'] == 'mouse_filter': + assert_equal(prop_info['usage'], PROPERTY_USAGE_NO_EDITOR) # Call simple methods. example.simple_func() diff --git a/test/src/example.cpp b/test/src/example.cpp index 8a761a4c..dc471dd2 100644 --- a/test/src/example.cpp +++ b/test/src/example.cpp @@ -117,6 +117,14 @@ bool Example::_property_get_revert(const StringName &p_name, Variant &r_property } }; +void Example::_validate_property(PropertyInfo &p_property) const { + String name = p_property.name; + // Test hiding the "mouse_filter" property from the editor. + if (name == "mouse_filter") { + p_property.usage = PROPERTY_USAGE_NO_EDITOR; + } +} + void Example::_bind_methods() { // Methods. ClassDB::bind_method(D_METHOD("simple_func"), &Example::simple_func); diff --git a/test/src/example.h b/test/src/example.h index 6e00b7f5..49d103e3 100644 --- a/test/src/example.h +++ b/test/src/example.h @@ -65,6 +65,7 @@ protected: void _get_property_list(List *p_list) const; bool _property_can_revert(const StringName &p_name) const; bool _property_get_revert(const StringName &p_name, Variant &r_property) const; + void _validate_property(PropertyInfo &p_property) const; String _to_string() const;