Jelajahi Sumber

Merge pull request #75778 from KoBeWi/_vp

Expose `_validate_property()` for scripting
Rémi Verschelde 2 tahun lalu
induk
melakukan
76d318dbd1

+ 6 - 0
core/object/object.cpp

@@ -526,6 +526,10 @@ void Object::get_property_list(List<PropertyInfo> *p_list, bool p_reversed) cons
 
 void Object::validate_property(PropertyInfo &p_property) const {
 	_validate_propertyv(p_property);
+
+	if (script_instance) { // Call it last to allow user altering already validated properties.
+		script_instance->validate_property(p_property);
+	}
 }
 
 bool Object::property_can_revert(const StringName &p_name) const {
@@ -1604,6 +1608,8 @@ void Object::_bind_methods() {
 	plget.return_val.hint_string = "Dictionary";
 	BIND_OBJ_CORE_METHOD(plget);
 
+	BIND_OBJ_CORE_METHOD(MethodInfo(Variant::NIL, "_validate_property", PropertyInfo(Variant::DICTIONARY, "property")));
+
 	BIND_OBJ_CORE_METHOD(MethodInfo(Variant::BOOL, "_property_can_revert", PropertyInfo(Variant::STRING_NAME, "property")));
 	MethodInfo mipgr("_property_get_revert", PropertyInfo(Variant::STRING_NAME, "property"));
 	mipgr.return_val.name = "Variant";

+ 2 - 0
core/object/script_language.h

@@ -180,6 +180,7 @@ public:
 	virtual bool get(const StringName &p_name, Variant &r_ret) const = 0;
 	virtual void get_property_list(List<PropertyInfo> *p_properties) const = 0;
 	virtual Variant::Type get_property_type(const StringName &p_name, bool *r_is_valid = nullptr) const = 0;
+	virtual void validate_property(PropertyInfo &p_property) const = 0;
 
 	virtual bool property_can_revert(const StringName &p_name) const = 0;
 	virtual bool property_get_revert(const StringName &p_name, Variant &r_ret) const = 0;
@@ -463,6 +464,7 @@ public:
 	virtual bool get(const StringName &p_name, Variant &r_ret) const override;
 	virtual void get_property_list(List<PropertyInfo> *p_properties) const override;
 	virtual Variant::Type get_property_type(const StringName &p_name, bool *r_is_valid = nullptr) const override;
+	virtual void validate_property(PropertyInfo &p_property) const override {}
 
 	virtual bool property_can_revert(const StringName &p_name) const override { return false; };
 	virtual bool property_get_revert(const StringName &p_name, Variant &r_ret) const override { return false; };

+ 3 - 0
core/object/script_language_extension.h

@@ -682,6 +682,9 @@ public:
 		}
 		return Variant::NIL;
 	}
+	virtual void validate_property(PropertyInfo &p_property) const override {
+		// TODO
+	}
 
 	virtual bool property_can_revert(const StringName &p_name) const override {
 		if (native_info->property_can_revert_func) {

+ 53 - 0
doc/classes/Object.xml

@@ -269,6 +269,59 @@
 				[/codeblock]
 			</description>
 		</method>
+		<method name="_validate_property" qualifiers="virtual">
+			<return type="void" />
+			<param index="0" name="property" type="Dictionary" />
+			<description>
+				Override this method to customize existing properties. Every property info goes through this method. The dictionary contents is the same as in [method _get_property_list].
+				[codeblocks]
+				[gdscript]
+				@tool
+				extends Node
+
+				@export var is_number_editable: bool:
+				    set(value):
+				        is_number_editable = value
+				        notify_property_list_changed()
+				@export var number: int
+
+				func _validate_property(property: Dictionary):
+				    if property.name == "number" and not is_number_editable:
+				        property.usage |= PROPERTY_USAGE_READ_ONLY
+				[/gdscript]
+				[csharp]
+				[Tool]
+				public partial class MyNode : Node
+				{
+				    private bool _isNumberEditable;
+
+				    [Export]
+				    public bool IsNumberEditable
+				    {
+				        get =&gt; _isNumberEditable;
+				        set
+				        {
+				            _isNumberEditable = value;
+				            NotifyPropertyListChanged();
+				        }
+				    }
+
+				    [Export]
+				    public int Number { get; set; }
+
+				    public override void _ValidateProperty(Godot.Collections.Dictionary property)
+				    {
+				        if (property["name"].AsStringName() == PropertyName.Number &amp;&amp; IsNumberEditable)
+				        {
+				            var usage = property["usage"].As&gt;PropertyUsageFlags&lt;() | PropertyUsageFlags.ReadOnly;
+				            property["usage"] = (int)usage;
+				        }
+				    }
+				}
+				[/csharp]
+				[/codeblocks]
+			</description>
+		</method>
 		<method name="add_user_signal">
 			<return type="void" />
 			<param index="0" name="signal" type="String" />

+ 22 - 1
modules/gdscript/gdscript.cpp

@@ -1732,6 +1732,25 @@ Variant::Type GDScriptInstance::get_property_type(const StringName &p_name, bool
 	return Variant::NIL;
 }
 
+void GDScriptInstance::validate_property(PropertyInfo &p_property) const {
+	Variant property = (Dictionary)p_property;
+	const Variant *args[1] = { &property };
+
+	const GDScript *sptr = script.ptr();
+	while (sptr) {
+		HashMap<StringName, GDScriptFunction *>::ConstIterator E = sptr->member_functions.find(GDScriptLanguage::get_singleton()->strings._validate_property);
+		if (E) {
+			Callable::CallError err;
+			Variant ret = E->value->call(const_cast<GDScriptInstance *>(this), args, 1, err);
+			if (err.error == Callable::CallError::CALL_OK) {
+				p_property = PropertyInfo::from_dict(property);
+				return;
+			}
+		}
+		sptr = sptr->_base;
+	}
+}
+
 void GDScriptInstance::get_property_list(List<PropertyInfo> *p_properties) const {
 	// exported members, not done yet!
 
@@ -1797,7 +1816,8 @@ void GDScriptInstance::get_property_list(List<PropertyInfo> *p_properties) const
 		p_properties->push_back(sptr->get_class_category());
 #endif // TOOLS_ENABLED
 
-		for (const PropertyInfo &prop : props) {
+		for (PropertyInfo &prop : props) {
+			validate_property(prop);
 			p_properties->push_back(prop);
 		}
 
@@ -2616,6 +2636,7 @@ GDScriptLanguage::GDScriptLanguage() {
 	strings._set = StaticCString::create("_set");
 	strings._get = StaticCString::create("_get");
 	strings._get_property_list = StaticCString::create("_get_property_list");
+	strings._validate_property = StaticCString::create("_validate_property");
 	strings._property_can_revert = StaticCString::create("_property_can_revert");
 	strings._property_get_revert = StaticCString::create("_property_get_revert");
 	strings._script_source = StaticCString::create("script/source");

+ 2 - 0
modules/gdscript/gdscript.h

@@ -321,6 +321,7 @@ public:
 	virtual bool get(const StringName &p_name, Variant &r_ret) const;
 	virtual void get_property_list(List<PropertyInfo> *p_properties) const;
 	virtual Variant::Type get_property_type(const StringName &p_name, bool *r_is_valid = nullptr) const;
+	virtual void validate_property(PropertyInfo &p_property) const;
 
 	virtual bool property_can_revert(const StringName &p_name) const;
 	virtual bool property_get_revert(const StringName &p_name, Variant &r_ret) const;
@@ -468,6 +469,7 @@ public:
 		StringName _set;
 		StringName _get;
 		StringName _get_property_list;
+		StringName _validate_property;
 		StringName _property_can_revert;
 		StringName _property_get_revert;
 		StringName _script_source;

+ 20 - 1
modules/mono/csharp_script.cpp

@@ -1656,7 +1656,8 @@ void CSharpInstance::get_property_list(List<PropertyInfo> *p_properties) const {
 		}
 	}
 
-	for (const PropertyInfo &prop : props) {
+	for (PropertyInfo &prop : props) {
+		validate_property(prop);
 		p_properties->push_back(prop);
 	}
 }
@@ -1694,6 +1695,24 @@ bool CSharpInstance::property_can_revert(const StringName &p_name) const {
 	return (bool)ret;
 }
 
+void CSharpInstance::validate_property(PropertyInfo &p_property) const {
+	ERR_FAIL_COND(!script.is_valid());
+
+	Variant property_arg = (Dictionary)p_property;
+	const Variant *args[1] = { &property_arg };
+
+	Variant ret;
+	Callable::CallError call_error;
+	GDMonoCache::managed_callbacks.CSharpInstanceBridge_Call(
+			gchandle.get_intptr(), &SNAME("_validate_property"), args, 1, &call_error, &ret);
+
+	if (call_error.error != Callable::CallError::CALL_OK) {
+		return;
+	}
+
+	p_property = PropertyInfo::from_dict(property_arg);
+}
+
 bool CSharpInstance::property_get_revert(const StringName &p_name, Variant &r_ret) const {
 	ERR_FAIL_COND_V(!script.is_valid(), false);
 

+ 1 - 0
modules/mono/csharp_script.h

@@ -258,6 +258,7 @@ public:
 	bool get(const StringName &p_name, Variant &r_ret) const override;
 	void get_property_list(List<PropertyInfo> *p_properties) const override;
 	Variant::Type get_property_type(const StringName &p_name, bool *r_is_valid) const override;
+	virtual void validate_property(PropertyInfo &p_property) const override;
 
 	bool property_can_revert(const StringName &p_name) const override;
 	bool property_get_revert(const StringName &p_name, Variant &r_ret) const override;

+ 2 - 0
tests/core/object/test_object.h

@@ -82,6 +82,8 @@ public:
 	Variant::Type get_property_type(const StringName &p_name, bool *r_is_valid) const override {
 		return Variant::PACKED_FLOAT32_ARRAY;
 	}
+	virtual void validate_property(PropertyInfo &p_property) const override {
+	}
 	bool property_can_revert(const StringName &p_name) const override {
 		return false;
 	};