Skip to content

Commit

Permalink
Add default export info
Browse files Browse the repository at this point in the history
Add ToTypeString
Add support for exporting nodes, resources, and arrays with type info
  • Loading branch information
lilizoey committed Apr 25, 2023
1 parent bab1070 commit b8995d0
Show file tree
Hide file tree
Showing 10 changed files with 249 additions and 99 deletions.
29 changes: 27 additions & 2 deletions godot-core/src/builtin/array.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ use godot_ffi as sys;

use crate::builtin::meta::VariantMetadata;
use crate::builtin::*;
use crate::obj::{Export, Share};
use crate::export::{Export, ExportDefaultInfo, TypeString};
use crate::obj::Share;
use std::fmt;
use std::marker::PhantomData;
use sys::{ffi_methods, interface_fn, GodotFfi};
Expand Down Expand Up @@ -620,7 +621,31 @@ impl<T: VariantMetadata> Share for Array<T> {
}
}

impl<T: VariantMetadata> Export for Array<T> {
impl<T: VariantMetadata + TypeString> ExportDefaultInfo for Array<T> {
fn default_property_hint() -> crate::engine::global::PropertyHint {
crate::engine::global::PropertyHint::PROPERTY_HINT_TYPE_STRING
}

fn default_property_hint_string() -> GodotString {
T::type_string().into()
}
}

impl ExportDefaultInfo for Array<Variant> {}

impl<T: VariantMetadata + TypeString> TypeString for Array<T> {
fn type_string() -> String {
format!("{}:{}", sys::VariantType::Array as i32, T::type_string())
}
}

impl<T: VariantMetadata + TypeString> Export for Array<T> {
fn export(&self) -> Self {
self.share()
}
}

impl Export for Array<Variant> {
fn export(&self) -> Self {
self.share()
}
Expand Down
5 changes: 4 additions & 1 deletion godot-core/src/builtin/dictionary.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
use godot_ffi as sys;

use crate::builtin::{inner, FromVariant, ToVariant, Variant};
use crate::obj::{Export, Share};
use crate::export::{Export, ExportDefaultInfo};
use crate::obj::Share;
use std::fmt;
use std::marker::PhantomData;
use std::ptr::addr_of_mut;
Expand Down Expand Up @@ -300,6 +301,8 @@ impl Share for Dictionary {
}
}

impl ExportDefaultInfo for Dictionary {}

impl Export for Dictionary {
fn export(&self) -> Self {
self.share()
Expand Down
4 changes: 4 additions & 0 deletions godot-core/src/builtin/meta/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@ impl<T: VariantMetadata> VariantMetadata for Option<T> {
fn variant_type() -> VariantType {
T::variant_type()
}

fn class_name() -> ClassName {
T::class_name()
}
}

// ----------------------------------------------------------------------------------------------------------------------------------------------
Expand Down
68 changes: 0 additions & 68 deletions godot-core/src/builtin/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -339,71 +339,3 @@ pub enum RectSide {
}

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

/// Implementations of the `Export` trait for types where it can be done trivially.
mod export {
use crate::builtin::*;
use crate::obj::Export;

macro_rules! impl_export_by_clone {
($ty:path) => {
impl Export for $ty {
fn export(&self) -> Self {
// If `Self` does not implement `Clone`, this gives a clearer error message
// than simply `self.clone()`.
Clone::clone(self)
}
}
};
}

impl_export_by_clone!(bool);
impl_export_by_clone!(isize);
impl_export_by_clone!(usize);
impl_export_by_clone!(i8);
impl_export_by_clone!(i16);
impl_export_by_clone!(i32);
impl_export_by_clone!(i64);
impl_export_by_clone!(u8);
impl_export_by_clone!(u16);
impl_export_by_clone!(u32);
impl_export_by_clone!(u64);
impl_export_by_clone!(f32);
impl_export_by_clone!(f64);

impl_export_by_clone!(Aabb);
impl_export_by_clone!(Basis);
impl_export_by_clone!(Color);
impl_export_by_clone!(GodotString);
impl_export_by_clone!(NodePath);
impl_export_by_clone!(PackedByteArray);
impl_export_by_clone!(PackedColorArray);
impl_export_by_clone!(PackedFloat32Array);
impl_export_by_clone!(PackedFloat64Array);
impl_export_by_clone!(PackedInt32Array);
impl_export_by_clone!(PackedInt64Array);
impl_export_by_clone!(PackedStringArray);
impl_export_by_clone!(PackedVector2Array);
impl_export_by_clone!(PackedVector3Array);
impl_export_by_clone!(Plane);
impl_export_by_clone!(Projection);
impl_export_by_clone!(Quaternion);
impl_export_by_clone!(Rect2);
impl_export_by_clone!(Rect2i);
impl_export_by_clone!(Rid);
impl_export_by_clone!(StringName);
impl_export_by_clone!(Transform2D);
impl_export_by_clone!(Transform3D);
impl_export_by_clone!(Vector2);
impl_export_by_clone!(Vector2i);
impl_export_by_clone!(Vector3);
impl_export_by_clone!(Vector3i);
impl_export_by_clone!(Vector4);

// Callables can be exported, however you can't do anything with them in the editor.
// But we do need to be able to export them since we can't make something a property without exporting.
// And it should be possible to access Callables by property from for instance GDScript.
impl_export_by_clone!(Callable);
// TODO investigate whether Signal should impl Export at all, and if so, how
// impl_export_by_clone!(Signal);
}
139 changes: 139 additions & 0 deletions godot-core/src/export.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/

use crate::{builtin::GodotString, engine::global::PropertyHint};

use godot_ffi as sys;

/// Trait implemented for types that can be used as `#[export]` fields. This creates a copy of the
/// value, for some type-specific definition of "copy". For example, `Array` and `Gd` are returned
/// via `Share::share()` instead of copying the actual data.
pub trait Export: ExportDefaultInfo {
/// Creates a copy to be returned from a getter.
fn export(&self) -> Self;
}

/// Trait implemented for types that can be used in an `#[export]` field without specifying a custom `hint`
/// and `hint_string`.
pub trait ExportDefaultInfo {
/// The property hint to use when exporting properties if a custom one isn't specified.
fn default_property_hint() -> PropertyHint {
PropertyHint::PROPERTY_HINT_NONE
}

/// The property hint string to use when exporting properties if a custom one isn't specified.
fn default_property_hint_string() -> GodotString {
GodotString::new()
}
}

impl<T: ExportDefaultInfo> ExportDefaultInfo for Option<T> {
fn default_property_hint() -> PropertyHint {
T::default_property_hint()
}

fn default_property_hint_string() -> GodotString {
T::default_property_hint_string()
}
}

impl<T: Export> Export for Option<T> {
fn export(&self) -> Self {
self.as_ref().map(|val| val.export())
}
}

// Implement ExportDefaultInfo for some various common types so people can use them without specifying a
// hint_type and hint_string.
impl ExportDefaultInfo for () {}
impl ExportDefaultInfo for String {}
impl ExportDefaultInfo for i128 {}
impl ExportDefaultInfo for isize {}
impl ExportDefaultInfo for u64 {}
impl ExportDefaultInfo for u128 {}
impl ExportDefaultInfo for usize {}

/// Trait for types that can be represented as a type string for use with
/// [`PropertyHint::PROPERTY_HINT_TYPE_STRING`].
pub trait TypeString {
/// Returns the representation of this type as a type string.
///
/// See [`PropertyHint.PROPERTY_HINT_TYPE_STRING`](
/// https://docs.godotengine.org/en/stable/classes/class_%40globalscope.html#enum-globalscope-propertyhint
/// ).
fn type_string() -> String;
}

mod export_impls {
use super::*;
use crate::builtin::*;

macro_rules! impl_export_by_clone {
($Ty:ty => $variant_type:ident) => {
impl ExportDefaultInfo for $Ty {}

impl Export for $Ty {
fn export(&self) -> Self {
// If `Self` does not implement `Clone`, this gives a clearer error message
// than simply `self.clone()`.
Clone::clone(self)
}
}

impl TypeString for $Ty {
fn type_string() -> String {
format!("{}:", sys::VariantType::$variant_type as i32)
}
}
};
}

impl_export_by_clone!(Aabb => Aabb);
impl_export_by_clone!(bool => Bool);
impl_export_by_clone!(Basis => Basis);
impl_export_by_clone!(Vector2 => Vector2);
impl_export_by_clone!(Vector3 => Vector3);
impl_export_by_clone!(Vector4 => Vector4);
impl_export_by_clone!(Vector2i => Vector2i);
impl_export_by_clone!(Vector3i => Vector3i);
impl_export_by_clone!(Quaternion => Quaternion);
impl_export_by_clone!(Color => Color);
impl_export_by_clone!(GodotString => String);
impl_export_by_clone!(StringName => StringName);
impl_export_by_clone!(NodePath => NodePath);
impl_export_by_clone!(PackedByteArray => PackedByteArray);
impl_export_by_clone!(PackedInt32Array => PackedInt32Array);
impl_export_by_clone!(PackedInt64Array => PackedInt64Array);
impl_export_by_clone!(PackedFloat32Array => PackedFloat32Array);
impl_export_by_clone!(PackedFloat64Array => PackedFloat64Array);
impl_export_by_clone!(PackedStringArray => PackedStringArray);
impl_export_by_clone!(PackedVector2Array => PackedVector2Array);
impl_export_by_clone!(PackedVector3Array => PackedVector3Array);
impl_export_by_clone!(PackedColorArray => PackedColorArray);
impl_export_by_clone!(Plane => Plane);
impl_export_by_clone!(Projection => Projection);
impl_export_by_clone!(Rid => Rid);
impl_export_by_clone!(Rect2 => Rect2);
impl_export_by_clone!(Rect2i => Rect2i);
impl_export_by_clone!(Transform2D => Transform2D);
impl_export_by_clone!(Transform3D => Transform3D);
impl_export_by_clone!(f64 => Float);
impl_export_by_clone!(f32 => Float);
impl_export_by_clone!(i64 => Int);
impl_export_by_clone!(i32 => Int);
impl_export_by_clone!(i16 => Int);
impl_export_by_clone!(i8 => Int);
impl_export_by_clone!(u32 => Int);
impl_export_by_clone!(u16 => Int);
impl_export_by_clone!(u8 => Int);

// Callables can be exported, however you can't do anything with them in the editor.
// But we do need to be able to export them since we can't make something a property without exporting.
// And it should be possible to access Callables by property from for instance GDScript.
impl_export_by_clone!(Callable => Callable);

// impl_export_by_clone!(Signal => Signal);
}
1 change: 1 addition & 0 deletions godot-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ mod storage;

pub mod builder;
pub mod builtin;
pub mod export;
pub mod init;
pub mod log;
pub mod macros;
Expand Down
57 changes: 55 additions & 2 deletions godot-core/src/obj/gd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,11 @@ use crate::builtin::meta::{ClassName, VariantMetadata};
use crate::builtin::{
Callable, FromVariant, GodotString, StringName, ToVariant, Variant, VariantConversionError,
};
use crate::engine::Object;
use crate::engine::{Node, Object, Resource};
use crate::export::{Export, ExportDefaultInfo, TypeString};
use crate::obj::dom::Domain as _;
use crate::obj::mem::Memory as _;
use crate::obj::{cap, dom, mem, Export, GodotClass, Inherits, Share};
use crate::obj::{cap, dom, mem, EngineEnum, GodotClass, Inherits, Share};
use crate::obj::{GdMut, GdRef, InstanceId};
use crate::storage::InstanceStorage;
use crate::{callbacks, engine, out};
Expand Down Expand Up @@ -444,6 +445,21 @@ impl<T: GodotClass> Gd<T> {
pub fn callable<S: Into<StringName>>(&self, method_name: S) -> Callable {
Callable::from_object_method(self.share(), method_name)
}

/// Returns whether `T` inherits from `U`.
///
/// This is reflexive, i.e `T` inherits from itself.
///
/// See also [`Inherits`] for a trait bound.
pub fn inherits<U: GodotClass>() -> bool {
if T::CLASS_NAME == U::CLASS_NAME {
true
} else if T::Base::CLASS_NAME == <()>::CLASS_NAME {
false
} else {
Gd::<T::Base>::inherits::<U>()
}
}
}

/// _The methods in this impl block are only available for objects `T` that are manually managed,
Expand Down Expand Up @@ -652,6 +668,43 @@ impl<T: GodotClass> Share for Gd<T> {
}
}

impl<T: GodotClass> TypeString for Gd<T> {
fn type_string() -> String {
use engine::global::PropertyHint;

match Self::default_property_hint() {
hint @ (PropertyHint::PROPERTY_HINT_RESOURCE_TYPE
| PropertyHint::PROPERTY_HINT_NODE_TYPE) => {
format!(
"{}/{}:{}",
VariantType::Object as i32,
hint.ord(),
T::CLASS_NAME
)
}
_ => format!("{}:", VariantType::Object as i32),
}
}
}

impl<T: GodotClass> ExportDefaultInfo for Gd<T> {
fn default_property_hint() -> engine::global::PropertyHint {
if Self::inherits::<Resource>() {
engine::global::PropertyHint::PROPERTY_HINT_RESOURCE_TYPE
} else if Self::inherits::<Node>() {
engine::global::PropertyHint::PROPERTY_HINT_NODE_TYPE
} else {
engine::global::PropertyHint::PROPERTY_HINT_NONE
}
}

fn default_property_hint_string() -> GodotString {
// Godot does this by default too, it doesn't seem to make a difference when not a resource/node
// but is needed when it is a resource/node.
T::CLASS_NAME.into()
}
}

impl<T: GodotClass> Export for Gd<T> {
fn export(&self) -> Self {
self.share()
Expand Down
8 changes: 0 additions & 8 deletions godot-core/src/obj/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,14 +52,6 @@ pub trait Share {
fn share(&self) -> Self;
}

/// Trait implemented for types that can be used as `#[export]` fields. This creates a copy of the
/// value, for some type-specific definition of "copy". For example, `Array` and `Gd` are returned
/// via `Share::share()` instead of copying the actual data.
pub trait Export {
/// Creates a copy to be returned from a getter.
fn export(&self) -> Self;
}

/// Non-strict inheritance relationship in the Godot class hierarchy.
///
/// `Derived: Inherits<Base>` means that either `Derived` is a subclass of `Base`, or the class `Base` itself (hence "non-strict").
Expand Down
Loading

0 comments on commit b8995d0

Please sign in to comment.