Skip to content

Commit 98a0eaa

Browse files
authored
Update docs from old #[after_set] etc. with new #[get, set] (#73)
Updating the Outdated API document as per issue #72
1 parent b7db212 commit 98a0eaa

File tree

1 file changed

+106
-39
lines changed

1 file changed

+106
-39
lines changed

src/rust-binding/properties.md

Lines changed: 106 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -37,52 +37,88 @@ If you need to hide this property in Godot editor, use `no_editor` option:
3737
enemy_count: i32,
3838
```
3939

40-
## Property access hooks
40+
## Property get/set
4141

42-
You can add hooks to call methods before and after property value was retrieved or changed.
42+
Properties can register `set` and `get` methods to be called from Godot.
4343

44-
Note that unlike [GDScript's `setget` keyword](https://docs.godotengine.org/en/3.3/getting_started/scripting/gdscript/gdscript_basics.html?#setters-getters), this does _not_ register a custom setter or getter. Instead, it registers a callback which is invoked _before or after_ the set/get occurs, and lacks both parameter and return value.
44+
Default get/set functions can be registered as per the following example:
45+
46+
```rs
4547

46-
```rust
4748
#[derive(NativeClass, Default)]
4849
#[inherit(Node)]
4950
struct GodotApi {
50-
// before get
51-
#[property(before_get = "Self::before_get")]
52-
prop_before_get: i32,
53-
54-
// before set
55-
#[property(before_set = "Self::before_set")]
56-
prop_before_set: i32,
51+
// property registration
52+
// Note: This is actually equivalent to #[property]
53+
#[property(get, set)]
54+
prop: i32,
55+
}
56+
```
5757

58-
// after get
59-
#[property(after_get = "Self::after_get")]
60-
prop_after_get: i32,
58+
If you need custom setters and getters, you can set them in the `property` attribute such as in the following example:
6159

62-
// after set
63-
#[property(after_set = "Self::after_set")]
64-
prop_after_set: i32,
60+
```rust
61+
#[derive(NativeClass)]
62+
#[inherit(Node)]
63+
struct HelloWorld {
64+
// property registration
65+
#[property(get = "Self::get", set = "Self::set")]
66+
prop: i32,
6567
}
6668

67-
impl GodotApi {
69+
impl HelloWorld {
6870
fn new(_owner: &Node) -> Self {
69-
Self::default()
71+
HelloWorld { prop: 0i32 }
7072
}
73+
}
7174

72-
fn before_get(&self, _owner: TRef<Node>) {
73-
godot_print!("Before get");
75+
#[methods]
76+
impl HelloWorld {
77+
fn get(&self, _owner: TRef<Node>) -> i32 {
78+
godot_print!("get() -> {}", &self.prop);
79+
self.prop
7480
}
7581

76-
fn before_set(&self, _owner: TRef<Node>) {
77-
godot_print!("Before set");
82+
fn set(&mut self, _owner: TRef<Node>, value: i32) {
83+
godot_print!("set({})", &value);
84+
self.prop = value;
85+
}
86+
}
87+
```
88+
89+
### Note: `get` vs `get_ref`
90+
91+
There are two ways to return the property.
92+
- `get` will return a value of `T` which _must_ result in the value being cloned.
93+
- `get_ref` must point to a function that returns `&T`, this is useful when working with large data that would be very expensive to copy unnecessarily.
94+
95+
Modifying the previous example accordingly results in the following:
96+
97+
```rust
98+
#[derive(NativeClass)]
99+
#[inherit(Node)]
100+
struct GodotApi {
101+
// property registration
102+
#[property(get_ref = "Self::get", set = "Self::set")]
103+
prop: String,
104+
}
105+
106+
impl GodotApi {
107+
fn new(_owner: &Node) -> Self {
108+
GodotApi { prop: String::new() }
78109
}
110+
}
79111

80-
fn after_get(&self, _owner: TRef<Node>) {
81-
godot_print!("After get");
112+
#[methods]
113+
impl GodotApi {
114+
fn get(&self, _owner: TRef<Node>) -> &String {
115+
godot_print!("get() -> {}", &self.prop);
116+
&self.prop
82117
}
83118

84-
fn after_set(&self, _owner: TRef<Node>) {
85-
godot_print!("After set");
119+
fn set(&mut self, _owner: TRef<Node>, value: String) {
120+
godot_print!("set({})", &value);
121+
self.prop = value;
86122
}
87123
}
88124
```
@@ -96,40 +132,42 @@ This is often the case where custom hint behavior is desired for primitive types
96132
To do so, you can use the [`ClassBuilder`](https://docs.rs/gdnative/latest/gdnative/prelude/struct.ClassBuilder.html) -- such as in the following examples -- to manually register each property and customize how they interface in the editor.
97133

98134
```rust
135+
99136
#[derive(NativeClass)]
100-
#[inherit(gdnative::api::Node)]
137+
#[inherit(Node)]
101138
#[register_with(Self::register_properties)]
139+
#[no_constructor]
102140
pub struct MyNode {
103141
number: i32,
104142
number_enum: i32,
105-
float_range: f32,
143+
float_range: f64,
106144
my_filepath: String,
107145
}
108146

109-
#[gdnative::methods]
147+
#[methods]
110148
impl MyNode {
111149
fn register_properties(builder: &ClassBuilder<MyNode>) {
112-
use gdnative::nativescript::property::StringHint;
150+
use gdnative::export::hint::*;
113151
// Add a number with a getter and setter.
114152
// (This is the equivalent of adding the `#[property]` attribute for `number`)
115153
builder
116-
.add_property::<i32>("number")
117-
.with_getter(number_getter)
118-
.with_setter(numer_setter)
154+
.property::<i32>("number")
155+
.with_getter(Self::number_getter)
156+
.with_setter(Self::number_setter)
119157
.done();
120158

121159
// Register the number as an Enum
122160
builder
123-
.add_property::<i32>("number_enum")
161+
.property::<i32>("number_enum")
124162
.with_getter(move |my_node: &MyNode, _owner: TRef<Node>| my_node.number_enum)
125163
.with_setter(move |my_node: &mut MyNode, _owner: TRef<Node>, new_value| my_node.number_enum = new_value)
126164
.with_default(1)
127-
.with_hint(IntHint::Enum(EnumHint::new("a", "b", "c", "d")))
165+
.with_hint(IntHint::Enum(EnumHint::new(vec!["a".to_owned(), "b".to_owned(), "c".to_owned(), "d".to_owned()])))
128166
.done();
129167

130168
// Register a floating point value with a range from 0.0 to 100.0 with a step of 0.1
131169
builder
132-
.add_property::<f64>("float_range")
170+
.property::<f64>("float_range")
133171
.with_getter(move |my_node: &MyNode, _owner: TRef<Node>| my_node.float_range)
134172
.with_setter(move |my_node: &mut MyNode, _owner: TRef<Node>, new_value| my_node.float_range = new_value)
135173
.with_default(1.0)
@@ -138,8 +176,8 @@ impl MyNode {
138176

139177
// Manually register a string as a file path for .txt and .dat files.
140178
builder
141-
.add_property::<String>("my_filepath")
142-
.with_getter(move |my_node: &MyNode, _owner: TRef<Node>| my_node.my_filepath.clone())
179+
.property::<String>("my_filepath")
180+
.with_ref_getter(move |my_node: &MyNode, _owner: TRef<Node>| &my_node.my_filepath)
143181
.with_setter(move |my_node: &mut MyNode, _owner: TRef<Node>, new_value: String| my_node.my_filepath = new_value)
144182
.with_default("".to_owned())
145183
.with_hint(StringHint::File(EnumHint::new(vec!["*.txt".to_owned(), "*.dat".to_owned()])))
@@ -154,3 +192,32 @@ impl MyNode {
154192
}
155193
}
156194
```
195+
196+
## `Property<T>` and when to use it
197+
198+
Sometimes it can be useful to expose a value as a property instead of as a function. Properties of this type serve as a _marker_ that can be registered with Godot and viewed in the editor without containing any data in Rust.
199+
200+
This can be useful for data (similar to the first sample) where the count serves more as a property of `enemies` rather than as its own distinct data, such as the following:
201+
202+
```rs
203+
struct Enemy {
204+
// Enemy Data
205+
}
206+
#[derive(NativeClass)]
207+
struct GodotApi {
208+
enemies: Vec<Enemy>,
209+
// Note: As the property is a "marker" property, this will never be used in code.
210+
#[allow(dead_code)]
211+
#[property(get = "Self::get_size")]
212+
enemy_count: Property<u32>,
213+
}
214+
215+
#[methods]
216+
impl GodotApi {
217+
//...
218+
219+
fn get_size(&self, _owner: TRef<Reference>) -> u32 {
220+
self.enemies.len() as u32
221+
}
222+
}
223+
```

0 commit comments

Comments
 (0)