diff --git a/Cargo.toml b/Cargo.toml index 2a12e52d7..73fa84073 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,6 +15,7 @@ members = [ "examples/resource", "examples/native_plugin", "examples/rpc", + "examples/array_export", "impl/proc_macros" ] diff --git a/examples/array_export/Cargo.toml b/examples/array_export/Cargo.toml new file mode 100644 index 000000000..d509e93c9 --- /dev/null +++ b/examples/array_export/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "array_export" +version = "0.1.0" +authors = ["The godot-rust developers"] +publish = false +edition = "2018" + +[lib] +crate-type = ["cdylib"] + +[dependencies] +gdnative = { path = "../../gdnative" } diff --git a/examples/array_export/ExportsArrays.gdns b/examples/array_export/ExportsArrays.gdns new file mode 100644 index 000000000..e4a63b7f9 --- /dev/null +++ b/examples/array_export/ExportsArrays.gdns @@ -0,0 +1,9 @@ +[gd_resource type="NativeScript" load_steps=2 format=2] + +[ext_resource path="res://array_export_library.gdnlib" type="GDNativeLibrary" id=1] + +[resource] +resource_name = "ExportsArrays" +class_name = "ExportsArrays" +library = ExtResource( 1 ) +script_class_name = "ExportsArrays" diff --git a/examples/array_export/ExportsArrays.tscn b/examples/array_export/ExportsArrays.tscn new file mode 100644 index 000000000..abb116912 --- /dev/null +++ b/examples/array_export/ExportsArrays.tscn @@ -0,0 +1,10 @@ +[gd_scene load_steps=2 format=2] + +[ext_resource path="res://ExportsArrays.gdns" type="Script" id=1] + +[node name="ExportsArrays" type="Node"] +script = ExtResource( 1 ) +single_array = [ 133.7, 1337, "Third Element" ] +single_array_range = [ -5, 1, 3, -3, 0 ] +double_array = [ [ 6, "(0, 1)" ], [ "Element 1, 0", 1375.5 ] ] +double_array_range = [ [ -2, 3 ], [ 4, -5 ] ] diff --git a/examples/array_export/array_export_library.gdnlib b/examples/array_export/array_export_library.gdnlib new file mode 100644 index 000000000..563c0c41e --- /dev/null +++ b/examples/array_export/array_export_library.gdnlib @@ -0,0 +1,17 @@ +[entry] + +X11.64="res://../../target/debug/libarray_export.so" +OSX.64="res://../../target/debug/libarray_export.dylib" +Windows.64="res://../../target/debug/array_export.dll" + +[dependencies] + +X11.64=[ ] +OSX.64=[ ] + +[general] + +singleton=false +load_once=true +symbol_prefix="godot_" +reloadable=true diff --git a/examples/array_export/default_env.tres b/examples/array_export/default_env.tres new file mode 100644 index 000000000..20207a4aa --- /dev/null +++ b/examples/array_export/default_env.tres @@ -0,0 +1,7 @@ +[gd_resource type="Environment" load_steps=2 format=2] + +[sub_resource type="ProceduralSky" id=1] + +[resource] +background_mode = 2 +background_sky = SubResource( 1 ) diff --git a/examples/array_export/icon.png b/examples/array_export/icon.png new file mode 100644 index 000000000..1d3cc3b28 Binary files /dev/null and b/examples/array_export/icon.png differ diff --git a/examples/array_export/icon.png.import b/examples/array_export/icon.png.import new file mode 100644 index 000000000..96cbf4629 --- /dev/null +++ b/examples/array_export/icon.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="StreamTexture" +path="res://.import/icon.png-487276ed1e3a0c39cad0279d744ee560.stex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://icon.png" +dest_files=[ "res://.import/icon.png-487276ed1e3a0c39cad0279d744ee560.stex" ] + +[params] + +compress/mode=0 +compress/lossy_quality=0.7 +compress/hdr_mode=0 +compress/bptc_ldr=0 +compress/normal_map=0 +flags/repeat=0 +flags/filter=true +flags/mipmaps=false +flags/anisotropic=false +flags/srgb=2 +process/fix_alpha_border=true +process/premult_alpha=false +process/HDR_as_SRGB=false +process/invert_color=false +stream=false +size_limit=0 +detect_3d=true +svg/scale=1.0 diff --git a/examples/array_export/project.godot b/examples/array_export/project.godot new file mode 100644 index 000000000..426f9138f --- /dev/null +++ b/examples/array_export/project.godot @@ -0,0 +1,32 @@ +; Engine configuration file. +; It's best edited using the editor UI and not directly, +; since the parameters that go here are not all obvious. +; +; Format: +; [section] ; section goes between [] +; param=value ; assign values to parameters + +config_version=4 + +_global_script_classes=[ { +"base": "", +"class": "ExportsArrays", +"language": "NativeScript", +"path": "res://ExportsArrays.gdns" +} ] +_global_script_class_icons={ +"ExportsArrays": "" +} + +[application] + +config/name="array_export" +run/main_scene="res://ExportsArrays.tscn" +config/icon="res://icon.png" + +[rendering] + +quality/driver/driver_name="GLES2" +vram_compression/import_etc=true +vram_compression/import_etc2=false +environment/default_environment="res://default_env.tres" diff --git a/examples/array_export/src/lib.rs b/examples/array_export/src/lib.rs new file mode 100644 index 000000000..b17b6d7a3 --- /dev/null +++ b/examples/array_export/src/lib.rs @@ -0,0 +1,61 @@ +use gdnative::nativescript::init::property::hint::{ArrayHint, IntHint, RangeHint}; +use gdnative::prelude::*; + +#[derive(NativeClass)] +#[inherit(Node)] +#[register_with(Self::register)] +struct ExportsArrays; + +#[gdnative::methods] +impl ExportsArrays { + fn new(_owner: &Node) -> Self { + ExportsArrays + } + + fn register(builder: &ClassBuilder) { + builder + .add_property::("single_array") + .with_setter(ExportsArrays::set_single_array) + .done(); + builder + .add_property::("single_array_range") + .with_setter(ExportsArrays::set_single_array_range) + .with_hint(ArrayHint::with_element_hint::(IntHint::Range( + RangeHint::new(-5, 5), + ))) + .done(); + builder + .add_property::("double_array") + .with_setter(ExportsArrays::set_double_array) + .with_hint(ArrayHint::with_element_hint::( + ArrayHint::new(), + )) + .done(); + builder + .add_property::("double_array_range") + .with_setter(ExportsArrays::set_double_array_range) + .with_hint(ArrayHint::with_element_hint::( + ArrayHint::with_element_hint::(IntHint::Range(RangeHint::new(-5, 5))), + )) + .done(); + } + + fn set_single_array(&mut self, _owner: TRef, value: VariantArray) { + godot_print!("Single: {:?}", value); + } + fn set_single_array_range(&mut self, _owner: TRef, value: VariantArray) { + godot_print!("Single Range: {:?}", value); + } + fn set_double_array(&mut self, _owner: TRef, value: VariantArray) { + godot_print!("Double: {:?}", value); + } + fn set_double_array_range(&mut self, _owner: TRef, value: VariantArray) { + godot_print!("Double Range: {:?}", value); + } +} + +fn init(handle: InitHandle) { + handle.add_class::(); +} + +godot_init!(init); diff --git a/gdnative-core/src/nativescript/init/property.rs b/gdnative-core/src/nativescript/init/property.rs index 7f7611f2d..6732e528c 100644 --- a/gdnative-core/src/nativescript/init/property.rs +++ b/gdnative-core/src/nativescript/init/property.rs @@ -407,7 +407,6 @@ mod impl_export { impl_export_for_core_type_without_hint!(NodePath); impl_export_for_core_type_without_hint!(Rid); impl_export_for_core_type_without_hint!(Dictionary); - impl_export_for_core_type_without_hint!(VariantArray); impl_export_for_core_type_without_hint!(ByteArray); impl_export_for_core_type_without_hint!(Int32Array); impl_export_for_core_type_without_hint!(Float32Array); @@ -460,4 +459,13 @@ mod impl_export { T::export_info(hint) } } + + impl Export for VariantArray { + type Hint = ArrayHint; + + #[inline] + fn export_info(hint: Option) -> ExportInfo { + hint.unwrap_or_default().export_info() + } + } } diff --git a/gdnative-core/src/nativescript/init/property/hint.rs b/gdnative-core/src/nativescript/init/property/hint.rs index ebae3f372..604db09c3 100644 --- a/gdnative-core/src/nativescript/init/property/hint.rs +++ b/gdnative-core/src/nativescript/init/property/hint.rs @@ -7,7 +7,7 @@ use crate::core_types::GodotString; use crate::core_types::VariantType; use crate::sys; -use super::ExportInfo; +use super::{Export, ExportInfo}; /// Hints that an integer or float property should be within an inclusive range. /// @@ -401,3 +401,78 @@ impl ColorHint { } } } + +/// Array hints optionally with an element hint. +#[derive(Debug, Default)] +pub struct ArrayHint { + element_hint: Option, +} + +impl ArrayHint { + /// Returns an `ArrayHint` without a element hint. + #[inline] + pub fn new() -> Self { + Self::default() + } + + /// Returns an `ArrayHint` with an element hint for type `T`, but without a hint for + /// that type. + #[inline] + pub fn with_element() -> Self { + Self::with_maybe_element_hint::(None) + } + + /// Returns an `ArrayHint` with an element hint for type `T`, and a hint for that type. + #[inline] + pub fn with_element_hint(hint: T::Hint) -> Self { + Self::with_maybe_element_hint::(Some(hint)) + } + + /// Returns an `ArrayHint` with an element hint for type `T`, and optionally a hint + /// for that type. + #[inline] + pub fn with_maybe_element_hint(hint: Option) -> Self { + ArrayHint { + element_hint: Some(T::export_info(hint)), + } + } +} + +impl ArrayHint { + #[inline] + pub fn export_info(self) -> ExportInfo { + if let Some(element_hint) = self.element_hint { + let hint_string = match (element_hint.variant_type, element_hint.hint_kind) { + // Special-cased because sub-hints seem to leave off the hint only if it's NONE, + // but Array will also do it on HINT_TYPE_STRING. + ( + VariantType::VariantArray, + sys::godot_property_hint_GODOT_PROPERTY_HINT_TYPE_STRING, + ) => format!( + "{}:{}", + VariantType::VariantArray as u32, + element_hint.hint_string + ), + (variant_type, sys::godot_property_hint_GODOT_PROPERTY_HINT_NONE) => { + format!("{}:{}", variant_type as u32, element_hint.hint_string) + } + (variant_type, hint_type) => format!( + "{}/{}:{}", + variant_type as u32, hint_type, element_hint.hint_string + ), + } + .into(); + ExportInfo { + variant_type: VariantType::VariantArray, + hint_kind: sys::godot_property_hint_GODOT_PROPERTY_HINT_TYPE_STRING, + hint_string, + } + } else { + ExportInfo { + variant_type: VariantType::VariantArray, + hint_kind: sys::godot_property_hint_GODOT_PROPERTY_HINT_NONE, + hint_string: GodotString::new(), + } + } + } +}