Skip to content

Add get_property_list, property_can_revert and property_get_revert virtual methods #621

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions godot-codegen/src/generator/virtual_traits.rs
Original file line number Diff line number Diff line change
@@ -104,6 +104,43 @@ fn special_virtual_methods(notification_enum_name: &Ident) -> TokenStream {
unimplemented!()
}

/// Called whenever the Godot engine needs to determine the properties an object has.
///
/// This method can be used to dynamically update the properties displayed by the editor depending on various conditions. This should
/// usually be combined with `#[class(tool)]` so that the code actually runs in the editor. Additionally if the property list changes
/// you need to call [`notify_property_list_changed`](crate::engine::Object::notify_property_list_changed) to actually notify the engine
/// that the property list has changed, otherwise nothing will appear to have happened.
///
/// The returned `Vec` cannot have more than `u32::MAX` or `usize::MAX - 1` elements, whichever is smaller.
///
/// See also in Godot docs:
/// * [`Object::_get_property_list`](https://docs.godotengine.org/en/stable/classes/class_object.html#class-object-private-method-get-property-list).
fn get_property_list(&self) -> Vec<PropertyInfo> {
unimplemented!()
}

/// Called by Godot to determine if a property has a custom default value.
///
/// Should return `true` when the property has a custom default value, otherwise should return `false`. Must be used in conjunction with
/// [`property_get_revert()`] to specify the custom default value.
///
/// See also in Godot docs:
/// * [`Object::_property_can_revert`](https://docs.godotengine.org/en/stable/classes/class_object.html#class-object-private-method-property-can-revert).
fn property_can_revert(&self, property: StringName) -> bool {
unimplemented!()
}

/// Called by Godot to determine the custom default value of a property.
///
/// Should return the given property's custom default value as `Some(value)`, or `None` if the given property doesn't have a custom
/// default value.
///
/// See also in Godot docs:
/// * [`Object::_property_get_revert`](https://docs.godotengine.org/en/stable/classes/class_object.html#class-object-private-method-property-get-revert).
fn property_get_revert(&self, property: StringName) -> Option<Variant> {
unimplemented!()
}

}
}

2 changes: 1 addition & 1 deletion godot-codegen/src/util.rs
Original file line number Diff line number Diff line change
@@ -22,7 +22,7 @@ pub fn make_imports() -> TokenStream {
quote! {
use godot_ffi as sys;
use crate::builtin::*;
use crate::builtin::meta::{ClassName, PtrcallReturnUnit, PtrcallReturnT, PtrcallReturnOptionGdT, PtrcallSignatureTuple, VarcallSignatureTuple};
use crate::builtin::meta::{ClassName, PtrcallReturnUnit, PtrcallReturnT, PtrcallReturnOptionGdT, PtrcallSignatureTuple, VarcallSignatureTuple, PropertyInfo};
use crate::engine::native::*;
use crate::engine::Object;
use crate::obj::Gd;
117 changes: 114 additions & 3 deletions godot-core/src/builtin/meta/mod.rs
Original file line number Diff line number Diff line change
@@ -23,6 +23,7 @@ pub(crate) use godot_convert::convert_error::*;

use crate::builtin::*;
use crate::engine::global;
use crate::property::{Export, PropertyHintInfo, Var};
use godot_ffi as sys;
use registration::method::MethodParamOrReturnInfo;
use sys::{GodotFfi, GodotNullableFfi};
@@ -241,21 +242,93 @@ where

// ----------------------------------------------------------------------------------------------------------------------------------------------

/// Rusty abstraction of `sys::GDExtensionPropertyInfo`.
/// Representation of a property in Godot.
///
/// Keeps the actual allocated values (the `sys` equivalent only keeps pointers, which fall out of scope).
/// Stores all info needed to inform Godot about how to interpret a property. This is used for actual properties, function arguments,
/// return types, signal arguments, and other similar cases.
///
/// A mismatch between property info and the actual type of a property may lead to runtime errors as Godot tries to use the property in the
/// wrong way, such as by inserting the wrong type or expecting a different type to be returned.
///
/// Rusty abstraction of `sys::GDExtensionPropertyInfo`, keeps the actual allocated values (the `sys` equivalent only keeps pointers, which
/// fall out of scope).
#[derive(Debug)]
// Note: is not #[non_exhaustive], so adding fields is a breaking change. Mostly used internally at the moment though.
// It is uncertain if we want to add more fields to this in the future, so we'll mark it `non_exhaustive` as a precautionary measure.
#[non_exhaustive]
pub struct PropertyInfo {
/// The variant type of the property.
///
/// Note that for classes this will be `Object`, and the `class_name` field will specify what specific class this property is.
pub variant_type: VariantType,
/// The class name of the property.
///
/// This only matters if `variant_type` is `Object`. Otherwise it's ignored by Godot.
pub class_name: ClassName,
/// The name this property will have in Godot.
pub property_name: StringName,
/// The property hint that will determine how Godot interprets this value.
///
/// See Godot docs for more information:
/// * [`PropertyHint`](https://docs.godotengine.org/en/stable/classes/class_%40globalscope.html#enum-globalscope-propertyhint).
pub hint: global::PropertyHint,
/// Extra information used in conjunction with `hint`.
pub hint_string: GString,
/// How Godot will use this property.
///
/// See Godot docs for more inormation:
/// * [`PropertyUsageFlags`](https://docs.godotengine.org/en/stable/classes/class_%40globalscope.html#enum-globalscope-propertyusageflags).
pub usage: global::PropertyUsageFlags,
}

impl PropertyInfo {
/// Create a new `PropertyInfo` for a property that isn't exported to the editor.
///
/// `P` is the type the property will be declared as, and `property_name` is the name the property will have.
pub fn new_var<P: Var>(property_name: &str) -> Self {
let PropertyHintInfo { hint, hint_string } = P::property_hint();

Self {
hint,
hint_string,
usage: global::PropertyUsageFlags::NO_EDITOR,
..P::Via::property_info(property_name)
}
}

/// Create a new `PropertyInfo` for a property that is exported to the editor.
///
/// `P` is the type the property will be declared as, and `property_name` is the name the property will have.
pub fn new_export<P: Export>(property_name: &str) -> Self {
let PropertyHintInfo { hint, hint_string } = P::default_export_info();

Self {
hint,
hint_string,
usage: global::PropertyUsageFlags::DEFAULT,
..P::Via::property_info(property_name)
}
}

/// Create a new `PropertyInfo` for the return type of a method.
///
/// `P` is the type the property will be declared as.
pub fn new_return<P: ToGodot>() -> Self {
Self {
usage: global::PropertyUsageFlags::NONE,
..P::Via::property_info("")
}
}

/// Create a new `PropertyInfo` for an argument of a method.
///
/// `P` is the type the property will be declared as, and `property_name` is the name the argument will have.
pub fn new_arg<P: FromGodot>(arg_name: &str) -> Self {
Self {
usage: global::PropertyUsageFlags::NONE,
..P::Via::property_info(arg_name)
}
}

/// Converts to the FFI type. Keep this object allocated while using that!
pub fn property_sys(&self) -> sys::GDExtensionPropertyInfo {
use crate::obj::EngineBitfield as _;
@@ -271,6 +344,44 @@ impl PropertyInfo {
}
}

/// Converts to the FFI type.
///
/// Unlike [`property_sys`](PropertyInfo::property_sys) this object does not need to be kept allocated while using the returned value,
/// however if you do not explicitly free the returned value at some point then this will lead to a memory leak. See
/// [`drop_property_sys`](PropertyInfo::drop_property_sys).
pub fn into_property_sys(self) -> sys::GDExtensionPropertyInfo {
use crate::obj::EngineBitfield as _;
use crate::obj::EngineEnum as _;

let Self {
variant_type,
class_name,
property_name,
hint,
hint_string,
usage,
} = self;

sys::GDExtensionPropertyInfo {
type_: variant_type.sys(),
name: property_name.into_string_sys(),
class_name: class_name.string_sys(),
hint: u32::try_from(hint.ord()).expect("hint.ord()"),
hint_string: hint_string.into_string_sys(),
usage: u32::try_from(usage.ord()).expect("usage.ord()"),
}
}

/// Consumes a [sys::GDExtensionPropertyInfo].
///
/// # Safety
///
/// The given property info must have been returned from a call to [`into_property_sys`](PropertyInfo::into_property_sys).
pub unsafe fn drop_property_sys(property_sys: sys::GDExtensionPropertyInfo) {
let _property_name = StringName::from_string_sys(property_sys.name);
let _hint_string = GString::from_string_sys(property_sys.hint_string);
}

pub fn empty_sys() -> sys::GDExtensionPropertyInfo {
use crate::obj::EngineBitfield as _;
use crate::obj::EngineEnum as _;
6 changes: 6 additions & 0 deletions godot-core/src/builtin/string/gstring.rs
Original file line number Diff line number Diff line change
@@ -142,6 +142,12 @@ impl GString {
pub fn as_inner(&self) -> inner::InnerString {
inner::InnerString::from_outer(self)
}

#[doc(hidden)]
pub fn into_string_sys(self) -> sys::GDExtensionStringPtr {
let string_name = Box::leak(Box::new(self));
string_name.string_sys()
}
}

// SAFETY:
6 changes: 6 additions & 0 deletions godot-core/src/builtin/string/string_name.rs
Original file line number Diff line number Diff line change
@@ -130,6 +130,12 @@ impl StringName {
inner::InnerStringName::from_outer(self)
}

#[doc(hidden)]
pub fn into_string_sys(self) -> sys::GDExtensionStringNamePtr {
let string_name = Box::leak(Box::new(self));
string_name.string_sys()
}

/// Increment ref-count. This may leak memory if used wrongly.
fn inc_ref(&self) {
std::mem::forget(self.clone());
19 changes: 19 additions & 0 deletions godot-core/src/obj/traits.rs
Original file line number Diff line number Diff line change
@@ -406,6 +406,7 @@ where
/// Capability traits, providing dedicated functionalities for Godot classes
pub mod cap {
use super::*;
use crate::builtin::meta::PropertyInfo;
use crate::builtin::{StringName, Variant};
use crate::obj::{Base, Bounds, Gd};

@@ -486,6 +487,24 @@ pub mod cap {
fn __godot_set_property(&mut self, property: StringName, value: Variant) -> bool;
}

#[doc(hidden)]
pub trait GodotGetPropertyList: GodotClass {
#[doc(hidden)]
fn __godot_get_property_list(&self) -> Vec<PropertyInfo>;
}

#[doc(hidden)]
pub trait GodotPropertyCanRevert: GodotClass {
#[doc(hidden)]
fn __godot_property_can_revert(&self, property: StringName) -> bool;
}

#[doc(hidden)]
pub trait GodotPropertyGetRevert: GodotClass {
#[doc(hidden)]
fn __godot_property_get_revert(&self, property: StringName) -> Option<Variant>;
}

/// Auto-implemented for `#[godot_api] impl MyClass` blocks
pub trait ImplementsGodotApi: GodotClass {
#[doc(hidden)]
Loading