Skip to content

Commit 89f5052

Browse files
authored
Merge pull request #707 from lilizoey/feature/get-property-list
Add `get_property_list`
2 parents 50ce26b + 54ac2fc commit 89f5052

File tree

12 files changed

+657
-1
lines changed

12 files changed

+657
-1
lines changed

godot-codegen/src/generator/virtual_traits.rs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,34 @@ fn make_special_virtual_methods(notification_enum_name: &Ident) -> TokenStream {
106106
unimplemented!()
107107
}
108108

109+
/// Called whenever Godot [`get_property_list()`](crate::engine::Object::get_property_list) is called, the returned vector here is
110+
/// appended to the existing list of properties.
111+
///
112+
/// This should mainly be used for advanced purposes, such as dynamically updating the property list in the editor.
113+
///
114+
/// See also in Godot docs:
115+
/// * [`Object::_get_property_list`](https://docs.godotengine.org/en/latest/classes/class_object.html#class-object-private-method-get-property-list)
116+
#[cfg(since_api = "4.3")]
117+
fn get_property_list(&mut self) -> Vec<crate::builtin::meta::PropertyInfo> {
118+
unimplemented!()
119+
}
120+
121+
/// Called by Godot to tell if a property has a custom revert or not.
122+
///
123+
/// Return `None` for no custom revert, and return `Some(value)` to specify the custom revert.
124+
///
125+
/// This is a combination of Godot's [`Object::_property_get_revert`] and [`Object::_property_can_revert`]. This means that this
126+
/// function will usually be called twice by Godot to find the revert.
127+
///
128+
/// Note that this should be a _pure_ function. That is, it should always return the same value for a property as long as `self`
129+
/// remains unchanged. Otherwise this may lead to unexpected (safe) behavior.
130+
///
131+
/// [`Object::_property_get_revert`]: https://docs.godotengine.org/en/latest/classes/class_object.html#class-object-private-method-property-get-revert
132+
/// [`Object::_property_can_revert`]: https://docs.godotengine.org/en/latest/classes/class_object.html#class-object-private-method-property-can-revert
133+
#[doc(alias = "property_can_revert")]
134+
fn property_get_revert(&self, property: StringName) -> Option<Variant> {
135+
unimplemented!()
136+
}
109137
}
110138
}
111139

godot-core/src/builtin/meta/mod.rs

Lines changed: 141 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,9 @@ pub use signature::*;
2121
pub(crate) use godot_convert::convert_error::*;
2222

2323
use crate::builtin::*;
24-
use crate::engine::global;
24+
use crate::engine::global::{self, PropertyHint, PropertyUsageFlags};
25+
use crate::property::Var;
26+
use crate::property::{Export, PropertyHintInfo};
2527
use godot_ffi as sys;
2628
use registration::method::MethodParamOrReturnInfo;
2729
use sys::{GodotFfi, GodotNullableFfi};
@@ -268,15 +270,119 @@ pub trait ArrayElement: GodotType {}
268270
#[derive(Debug, Clone)]
269271
// Note: is not #[non_exhaustive], so adding fields is a breaking change. Mostly used internally at the moment though.
270272
pub struct PropertyInfo {
273+
/// Which type this property has.
274+
///
275+
/// For objects this should be set to [`VariantType::Object`], and the `class_name` field to the actual name of the class.
276+
///
277+
/// For [`Variant`] this should be set to [`VariantType::Nil`].
271278
pub variant_type: VariantType,
279+
280+
/// Which class this property is.
281+
///
282+
/// This should be set to [`ClassName::none()`] unless the variant type is `Object`. You can use
283+
/// [`GodotClass::class_name()`](crate::obj::GodotClass::class_name()) to get the right name to use here.
272284
pub class_name: ClassName,
285+
286+
/// The name of this property in Godot.
273287
pub property_name: StringName,
288+
289+
/// How the property is meant to be edited. See also [`PropertyHint`] in the Godot docs.
290+
///
291+
/// [`PropertyHint`]: https://docs.godotengine.org/en/latest/classes/class_%40globalscope.html#enum-globalscope-propertyhint
274292
pub hint: global::PropertyHint,
293+
294+
/// Extra information passed to Godot for this property, what this means depends on the `hint` value.
275295
pub hint_string: GString,
296+
297+
/// How this property should be used. See [`PropertyUsageFlags`] in Godot for the meaning.
298+
///
299+
/// [`PropertyUsageFlags`]: https://docs.godotengine.org/en/latest/classes/class_%40globalscope.html#enum-globalscope-propertyusageflags
276300
pub usage: global::PropertyUsageFlags,
277301
}
278302

279303
impl PropertyInfo {
304+
/// Create a new `PropertyInfo` representing a property named `property_name` with type `T`.
305+
///
306+
/// This will generate property info equivalent to what a `#[var]` attribute would.
307+
pub fn new_var<T: Var>(property_name: &str) -> Self {
308+
<T as GodotConvert>::Via::property_info(property_name).with_hint_info(T::property_hint())
309+
}
310+
311+
/// Create a new `PropertyInfo` representing an exported property named `property_name` with type `T`.
312+
///
313+
/// This will generate property info equivalent to what an `#[export]` attribute would.
314+
pub fn new_export<T: Export>(property_name: &str) -> Self {
315+
<T as GodotConvert>::Via::property_info(property_name)
316+
.with_hint_info(T::default_export_info())
317+
}
318+
319+
/// Change the `hint` and `hint_string` to be the given `hint_info`.
320+
///
321+
/// See [`export_info_functions`](crate::property::export_info_functions) for functions that return appropriate `PropertyHintInfo`s for
322+
/// various Godot annotations.
323+
///
324+
/// # Examples
325+
///
326+
/// Creating an `@export_range` property.
327+
///
328+
// TODO: Make this nicer to use.
329+
/// ```no_run
330+
/// use godot::register::property::export_info_functions;
331+
/// use godot::builtin::meta::PropertyInfo;
332+
///
333+
/// let property = PropertyInfo::new_export::<f64>("my_range_property")
334+
/// .with_hint_info(export_info_functions::export_range(
335+
/// 0.0,
336+
/// 10.0,
337+
/// Some(0.1),
338+
/// false,
339+
/// false,
340+
/// false,
341+
/// false,
342+
/// false,
343+
/// false,
344+
/// ));
345+
/// ```
346+
pub fn with_hint_info(self, hint_info: PropertyHintInfo) -> Self {
347+
let PropertyHintInfo { hint, hint_string } = hint_info;
348+
349+
Self {
350+
hint,
351+
hint_string,
352+
..self
353+
}
354+
}
355+
356+
/// Create a new `PropertyInfo` representing a group in Godot.
357+
///
358+
/// See [`EditorInspector`](https://docs.godotengine.org/en/latest/classes/class_editorinspector.html#class-editorinspector) in Godot for
359+
/// more information.
360+
pub fn new_group(group_name: &str, group_prefix: &str) -> Self {
361+
Self {
362+
variant_type: VariantType::Nil,
363+
class_name: ClassName::none(),
364+
property_name: group_name.into(),
365+
hint: PropertyHint::NONE,
366+
hint_string: group_prefix.into(),
367+
usage: PropertyUsageFlags::GROUP,
368+
}
369+
}
370+
371+
/// Create a new `PropertyInfo` representing a subgroup in Godot.
372+
///
373+
/// See [`EditorInspector`](https://docs.godotengine.org/en/latest/classes/class_editorinspector.html#class-editorinspector) in Godot for
374+
/// more information.
375+
pub fn new_subgroup(subgroup_name: &str, subgroup_prefix: &str) -> Self {
376+
Self {
377+
variant_type: VariantType::Nil,
378+
class_name: ClassName::none(),
379+
property_name: subgroup_name.into(),
380+
hint: PropertyHint::NONE,
381+
hint_string: subgroup_prefix.into(),
382+
usage: PropertyUsageFlags::SUBGROUP,
383+
}
384+
}
385+
280386
/// Converts to the FFI type. Keep this object allocated while using that!
281387
pub fn property_sys(&self) -> sys::GDExtensionPropertyInfo {
282388
use crate::obj::EngineBitfield as _;
@@ -305,6 +411,40 @@ impl PropertyInfo {
305411
usage: global::PropertyUsageFlags::NONE.ord() as u32,
306412
}
307413
}
414+
415+
/// Consumes self and turns it into a `sys::GDExtensionPropertyInfo`, should be used together with
416+
/// [`free_owned_property_sys`](Self::free_owned_property_sys).
417+
///
418+
/// This will leak memory unless used together with `free_owned_property_sys`.
419+
pub(crate) fn into_owned_property_sys(self) -> sys::GDExtensionPropertyInfo {
420+
use crate::obj::EngineBitfield as _;
421+
use crate::obj::EngineEnum as _;
422+
423+
sys::GDExtensionPropertyInfo {
424+
type_: self.variant_type.sys(),
425+
name: self.property_name.into_owned_string_sys(),
426+
class_name: sys::SysPtr::force_mut(self.class_name.string_sys()),
427+
hint: u32::try_from(self.hint.ord()).expect("hint.ord()"),
428+
hint_string: self.hint_string.into_owned_string_sys(),
429+
usage: u32::try_from(self.usage.ord()).expect("usage.ord()"),
430+
}
431+
}
432+
433+
/// Properly frees a `sys::GDExtensionPropertyInfo` created by [`into_owned_property_sys`](Self::into_owned_property_sys).
434+
///
435+
/// # Safety
436+
///
437+
/// * Must only be used on a struct returned from a call to `into_owned_property_sys`, without modification.
438+
/// * Must not be called more than once on a `sys::GDExtensionPropertyInfo` struct.
439+
pub(crate) unsafe fn free_owned_property_sys(info: sys::GDExtensionPropertyInfo) {
440+
// SAFETY: This function was called on a pointer returned from `into_owned_property_sys`, thus both `info.name` and
441+
// `info.hint_string` were created from calls to `into_owned_string_sys` on their respective types.
442+
// Additionally this function isn't called more than once on a struct containing the same `name` or `hint_string` pointers.
443+
unsafe {
444+
let _name = StringName::from_owned_string_sys(info.name);
445+
let _hint_string = GString::from_owned_string_sys(info.hint_string);
446+
}
447+
}
308448
}
309449

310450
// ----------------------------------------------------------------------------------------------------------------------------------------------

godot-core/src/builtin/string/gstring.rs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,34 @@ impl GString {
154154
fn string_sys_mut = sys_mut;
155155
}
156156

157+
/// Consumes self and turns it into a sys-ptr, should be used together with [`from_owned_string_sys`](Self::from_owned_string_sys).
158+
///
159+
/// This will leak memory unless `from_owned_string_sys` is called on the returned pointer.
160+
pub(crate) fn into_owned_string_sys(self) -> sys::GDExtensionStringPtr {
161+
sys::static_assert_eq_size_align!(StringName, sys::types::OpaqueString);
162+
163+
let leaked = Box::into_raw(Box::new(self));
164+
leaked.cast()
165+
}
166+
167+
/// Creates a `GString` from a sys-ptr without incrementing the refcount.
168+
///
169+
/// # Safety
170+
///
171+
/// * Must only be used on a pointer returned from a call to [`into_owned_string_sys`](Self::into_owned_string_sys).
172+
/// * Must not be called more than once on the same pointer.
173+
#[deny(unsafe_op_in_unsafe_fn)]
174+
pub(crate) unsafe fn from_owned_string_sys(ptr: sys::GDExtensionStringPtr) -> Self {
175+
sys::static_assert_eq_size_align!(StringName, sys::types::OpaqueString);
176+
177+
let ptr = ptr.cast::<Self>();
178+
179+
// SAFETY: `ptr` was returned from a call to `into_owned_string_sys`, which means it was created by a call to
180+
// `Box::into_raw`, thus we can use `Box::from_raw` here. Additionally this is only called once on this pointer.
181+
let boxed = unsafe { Box::from_raw(ptr) };
182+
*boxed
183+
}
184+
157185
/// Moves this string into a string sys pointer. This is the same as using [`GodotFfi::move_return_ptr`].
158186
///
159187
/// # Safety

godot-core/src/builtin/string/string_name.rs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,34 @@ impl StringName {
113113
fn string_sys_mut = sys_mut;
114114
}
115115

116+
/// Consumes self and turns it into a sys-ptr, should be used together with [`from_owned_string_sys`](Self::from_owned_string_sys).
117+
///
118+
/// This will leak memory unless `from_owned_string_sys` is called on the returned pointer.
119+
pub(crate) fn into_owned_string_sys(self) -> sys::GDExtensionStringNamePtr {
120+
sys::static_assert_eq_size_align!(StringName, sys::types::OpaqueStringName);
121+
122+
let leaked = Box::into_raw(Box::new(self));
123+
leaked.cast()
124+
}
125+
126+
/// Creates a `StringName` from a sys-ptr without incrementing the refcount.
127+
///
128+
/// # Safety
129+
///
130+
/// * Must only be used on a pointer returned from a call to [`into_owned_string_sys`](Self::into_owned_string_sys).
131+
/// * Must not be called more than once on the same pointer.
132+
#[deny(unsafe_op_in_unsafe_fn)]
133+
pub(crate) unsafe fn from_owned_string_sys(ptr: sys::GDExtensionStringNamePtr) -> Self {
134+
sys::static_assert_eq_size_align!(StringName, sys::types::OpaqueStringName);
135+
136+
let ptr = ptr.cast::<Self>();
137+
138+
// SAFETY: `ptr` was returned from a call to `into_owned_string_sys`, which means it was created by a call to
139+
// `Box::into_raw`, thus we can use `Box::from_raw` here. Additionally this is only called once on this pointer.
140+
let boxed = unsafe { Box::from_raw(ptr) };
141+
*boxed
142+
}
143+
116144
/// Convert a `StringName` sys pointer to a reference with unbounded lifetime.
117145
///
118146
/// # Safety

godot-core/src/obj/traits.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -523,6 +523,18 @@ pub mod cap {
523523
fn __godot_set_property(&mut self, property: StringName, value: Variant) -> bool;
524524
}
525525

526+
#[doc(hidden)]
527+
pub trait GodotGetPropertyList: GodotClass {
528+
#[doc(hidden)]
529+
fn __godot_get_property_list(&mut self) -> Vec<crate::builtin::meta::PropertyInfo>;
530+
}
531+
532+
#[doc(hidden)]
533+
pub trait GodotPropertyGetRevert: GodotClass {
534+
#[doc(hidden)]
535+
fn __godot_property_get_revert(&self, property: StringName) -> Option<Variant>;
536+
}
537+
526538
/// Auto-implemented for `#[godot_api] impl MyClass` blocks
527539
pub trait ImplementsGodotApi: GodotClass {
528540
#[doc(hidden)]

0 commit comments

Comments
 (0)