godot_core/obj/on_editor.rs
1/*
2 * Copyright (c) godot-rust; Bromeon and contributors.
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at https://mozilla.org/MPL/2.0/.
6 */
7
8use crate::meta::shape::GodotShape;
9use crate::meta::{FromGodot, GodotConvert, GodotType};
10use crate::registry::property::{BuiltinExport, Export, Var};
11
12/// Exported property that must be initialized in the editor (or associated code) before use.
13///
14/// Use this type whenever your Rust code cannot provide a value for a field, but expects one to be specified in the Godot editor.
15///
16/// If you need automatic initialization during `ready()`, e.g. for loading nodes or resources, use [`OnReady<Gd<T>>`](crate::obj::OnReady)
17/// instead. As a general "maybe initialized" type, `Option<Gd<T>>` is always available, even if more verbose.
18///
19///
20/// # What constitutes "initialized"?
21/// Whether a value is considered initialized or not depends on `T`.
22///
23/// - For objects, a value is initialized if it is not null. Exported object propreties in Godot are nullable, but `Gd<T>` and `DynGd<T, D>` do
24/// not support nullability and can thus not directly be exported with `#[export]`. `OnEditor` can bridge this gap, by expecting users
25/// to set a non-null value, and panicking if they don't.
26/// - For built-in types, a value is initialized if it is different from a user-selected sentinel value (e.g. `-1`).
27///
28/// More on this below (see also table-of-contents sidebar).
29///
30/// # Initialization semantics
31/// Panics during access (`Deref/DerefMut` impls) if uninitialized.
32///
33/// When used inside a node class, `OnEditor` checks if a value has been set before `ready()` is run, and panics otherwise.
34/// This validation is performed for all `OnEditor` fields declared in a given `GodotClass`, regardless of whether they are `#[var]`, `#[export]`, or neither.
35/// Once initialized, `OnEditor` can be used almost as if it were a `T` value itself, due to `Deref`/`DerefMut` impls.
36///
37/// `OnEditor<T>` should always be used as a struct field, preferably in tandem with an `#[export]` or `#[var]`.
38/// Initializing `OnEditor` values via code before the first use is supported, but should be limited to use cases involving builder or factory patterns.
39///
40///
41/// # Using `OnEditor` with classes
42/// You can wrap class smart pointers `Gd<T>` and `DynGd<T, D>` inside `OnEditor`, to make them exportable.
43/// `Gd<T>` itself does not implement the `Export` trait.
44///
45/// ## Example: automatic init
46/// This example uses the `Default` impl, which expects a non-null value to be provided.
47///
48/// ```
49/// # use godot::prelude::*;
50/// #[derive(GodotClass)]
51/// #[class(init, base = Node)]
52/// struct ResourceHolder {
53/// #[export]
54/// editor_property: OnEditor<Gd<Resource>>,
55/// }
56///
57/// #[godot_api]
58/// impl INode for ResourceHolder {
59/// fn ready(&mut self) {
60/// // Will always be valid and **must** be set via editor.
61/// // Additional check is being run before ready(),
62/// // to ensure that given value can't be null.
63/// let some_variant = self.editor_property.get_meta("SomeName");
64/// }
65/// }
66/// ```
67///
68/// ## Example: user-defined init
69/// Uninitialized `OnEditor<Gd<T>>` and `OnEditor<DynGd<T, D>>` can be created with `OnEditor::default()`.
70///
71/// ```
72/// # use godot::prelude::*;
73/// #[derive(GodotClass)]
74/// #[class(base = Node)]
75/// struct NodeHolder {
76/// #[export]
77/// required_node: OnEditor<Gd<Node>>,
78///
79/// base: Base<Node>
80/// }
81///
82/// #[godot_api]
83/// impl INode for NodeHolder {
84/// fn init(base: Base<Node>) -> Self {
85/// Self {
86/// base,
87/// required_node: OnEditor::default(),
88/// }
89/// }
90/// }
91///```
92///
93/// ## Example: factory pattern
94///
95/// ```
96/// # use godot::prelude::*;
97/// #[derive(GodotClass)]
98/// #[class(init, base = Node)]
99/// struct NodeHolder {
100/// #[export]
101/// required_node: OnEditor<Gd<Node>>,
102/// }
103///
104/// fn create_and_add(
105/// mut this: Gd<Node>,
106/// some_class_scene: Gd<PackedScene>,
107/// some_node: Gd<Node>,
108/// ) -> Gd<NodeHolder> {
109/// let mut my_node = some_class_scene.instantiate_as::<NodeHolder>();
110///
111/// // Would cause a panic:
112/// // this.add_child(&my_node);
113///
114/// // It's possible to initialize the value programmatically, although typically
115/// // it is set in the editor and stored in a .tscn file.
116/// // Note: nodes are manually managed and leak memory unless tree-attached or freed.
117/// my_node.bind_mut().required_node.init(some_node);
118///
119/// // Will not panic, since the node is initialized now.
120/// this.add_child(&my_node);
121///
122/// my_node
123/// }
124/// ```
125///
126/// # Using `OnEditor` with built-in types
127/// `OnEditor<T>` can be used with any `#[export]`-enabled builtins, to provide domain-specific validation logic.
128/// An example might be to check whether a game entity has been granted a non-zero ID.
129///
130/// To detect whether a value has been set in the editor, `OnEditor<T>` uses a _sentinel value_. This is a special marker value for
131/// "uninitialized" and is selected by the user. For example, a sentinel value of `-1` or `0` might be used to represent an uninitialized `i32`.
132///
133/// There is deliberately no `Default` implementation for `OnEditor` with builtins, as the sentinel is highly domain-specific.
134///
135/// ## Example
136///
137/// ```
138/// use godot::prelude::*;
139///
140/// #[derive(GodotClass)]
141/// #[class(init, base = Node)]
142/// struct IntHolder {
143/// // Uninitialized value will be represented by `42` in the editor.
144/// // Will cause panic if not set via the editor or code before use.
145/// #[export]
146/// #[init(sentinel = 42)]
147/// some_primitive: OnEditor<i64>,
148/// }
149///
150/// fn create_and_add(mut this: Gd<Node>, val: i64) -> Gd<IntHolder> {
151/// let mut my_node = IntHolder::new_alloc();
152///
153/// // Would cause a panic:
154/// // this.add_child(&my_node);
155///
156/// // It's possible to initialize the value programmatically, although typically
157/// // it is set in the editor and stored in a .tscn file.
158/// my_node.bind_mut().some_primitive.init(val);
159///
160/// // Will not panic, since the node is initialized now.
161/// this.add_child(&my_node);
162///
163/// my_node
164/// }
165/// ```
166///
167/// # Custom getters and setters for `OnEditor`
168/// Custom setters and/or getters for `OnEditor` are declared by accepting/returning the inner type.
169/// In their implementation, delegate to the `Var` trait methods, rather than dereferencing directly.
170///
171/// ```
172/// # use godot::prelude::*;
173/// #[derive(GodotClass)]
174/// #[class(init)]
175/// struct MyStruct {
176/// #[var(get, set)]
177/// my_node: OnEditor<Gd<Node>>,
178///
179/// #[var(get, set)]
180/// #[init(sentinel = -1)]
181/// my_value: OnEditor<i32>,
182/// }
183///
184/// #[godot_api]
185/// impl MyStruct {
186/// #[func]
187/// pub fn get_my_node(&self) -> Gd<Node> {
188/// let ret = Var::var_pub_get(&self.my_node);
189/// // Do something with the value...
190/// ret
191/// }
192///
193/// #[func]
194/// pub fn set_my_node(&mut self, value: Gd<Node>) {
195/// // Validate, pre-process, etc...
196/// Var::var_pub_set(&mut self.my_node, value);
197/// }
198///
199/// #[func]
200/// pub fn get_my_value(&self) -> i32 {
201/// let ret = Var::var_pub_get(&self.my_value);
202/// // Do something with the value...
203/// ret
204/// }
205///
206/// #[func]
207/// pub fn set_my_value(&mut self, value: i32) {
208/// if value == 13 {
209/// godot_warn!("13 is unlucky number.");
210/// return;
211/// }
212///
213/// Var::var_pub_set(&mut self.my_value, value);
214/// }
215/// }
216/// ```
217///
218/// See also: [Register properties -- `#[var]`](../register/derive.GodotClass.html#register-properties--var)
219///
220/// # Using `OnEditor` with `#[class(tool)]`
221/// When used with `#[class(tool)]`, the before-ready checks are omitted.
222/// Otherwise, `OnEditor<T>` behaves the same — accessing an uninitialized value will cause a panic.
223#[derive(Debug)]
224pub struct OnEditor<T> {
225 state: OnEditorState<T>,
226}
227
228#[derive(Debug)]
229pub(crate) enum OnEditorState<T> {
230 /// Uninitialized null value.
231 UninitNull,
232 /// Uninitialized state, but with a value marked as invalid (required to represent non-nullable type in the editor).
233 UninitSentinel(T),
234 /// Initialized with a value.
235 Initialized(T),
236}
237
238/// `OnEditor<T>` is usable only for properties – which is enforced via `Var` and `FromGodot` bounds.
239///
240/// Furthermore, `PartialEq` is needed to compare against uninitialized sentinel values.
241impl<T: Var + FromGodot + PartialEq> OnEditor<T> {
242 /// Initializes invalid `OnEditor<T>` with given value.
243 ///
244 /// # Panics
245 /// If `init()` was called before.
246 pub fn init(&mut self, val: T) {
247 match self.state {
248 OnEditorState::UninitNull | OnEditorState::UninitSentinel(_) => {
249 *self = OnEditor {
250 state: OnEditorState::Initialized(val),
251 };
252 }
253 OnEditorState::Initialized(_) => {
254 panic!(
255 "Given OnEditor value has been already initialized; did you call init() more than once?"
256 )
257 }
258 }
259 }
260
261 /// Creates new `OnEditor<T>` with a value that is considered invalid.
262 ///
263 /// If this value is not changed in the editor, accessing it from Rust will cause a panic.
264 pub fn from_sentinel(val: T) -> Self
265 where
266 T::Via: BuiltinExport,
267 {
268 OnEditor {
269 state: OnEditorState::UninitSentinel(val),
270 }
271 }
272
273 /// Creates new uninitialized `OnEditor<T>` value for nullable GodotTypes.
274 ///
275 /// Not a part of public API – available only via `Default` implementation on `OnEditor<Gd<T>>` and `OnEditor<DynGd<D, T>>`.
276 pub(crate) fn gd_invalid() -> Self {
277 OnEditor {
278 state: OnEditorState::UninitNull,
279 }
280 }
281
282 #[doc(hidden)]
283 pub fn is_invalid(&self) -> bool {
284 match self.state {
285 OnEditorState::UninitNull | OnEditorState::UninitSentinel(_) => true,
286 OnEditorState::Initialized(_) => false,
287 }
288 }
289
290 /// `Var::get_property` implementation that works both for nullable and non-nullable types.
291 pub(crate) fn get_property_inner(&self) -> Option<T::Via> {
292 match &self.state {
293 OnEditorState::UninitNull => None,
294 OnEditorState::UninitSentinel(val) | OnEditorState::Initialized(val) => {
295 Some(T::var_get(val))
296 }
297 }
298 }
299
300 /// [`Var::var_set`] implementation that works both for nullable and non-nullable types.
301 ///
302 /// All the state transitions are valid, since it is being run only in the editor.
303 /// See also [`Option::var_set()`].
304 pub(crate) fn set_property_inner(&mut self, value: Option<T::Via>) {
305 match (value, &mut self.state) {
306 (None, _) => self.state = OnEditorState::UninitNull,
307 (Some(value), OnEditorState::Initialized(current_value)) => {
308 T::var_set(current_value, value);
309 }
310 (Some(value), OnEditorState::UninitNull) => {
311 self.state = OnEditorState::Initialized(FromGodot::from_godot(value))
312 }
313 (Some(value), OnEditorState::UninitSentinel(current_value)) => {
314 let value = FromGodot::from_godot(value);
315 if value != *current_value {
316 self.state = OnEditorState::Initialized(value)
317 }
318 }
319 }
320 }
321}
322
323impl<T> std::ops::Deref for OnEditor<T> {
324 type Target = T;
325 fn deref(&self) -> &Self::Target {
326 match &self.state {
327 OnEditorState::UninitNull | OnEditorState::UninitSentinel(_) => {
328 panic!("OnEditor field hasn't been initialized.")
329 }
330 OnEditorState::Initialized(v) => v,
331 }
332 }
333}
334
335impl<T> std::ops::DerefMut for OnEditor<T> {
336 fn deref_mut(&mut self) -> &mut Self::Target {
337 match &mut self.state {
338 OnEditorState::UninitNull | OnEditorState::UninitSentinel(_) => {
339 panic!("OnEditor field hasn't been initialized.")
340 }
341 OnEditorState::Initialized(v) => v,
342 }
343 }
344}
345
346impl<T> GodotConvert for OnEditor<T>
347where
348 T: GodotConvert,
349 T::Via: GodotType + BuiltinExport,
350{
351 type Via = T::Via;
352
353 fn godot_shape() -> GodotShape {
354 T::godot_shape()
355 }
356}
357
358impl<T> Var for OnEditor<T>
359where
360 OnEditor<T>: GodotConvert<Via = T::Via>,
361 T: Var + FromGodot + PartialEq,
362 T::Via: BuiltinExport,
363{
364 type PubType = T::Via;
365
366 fn var_get(field: &Self) -> Self::Via {
367 field
368 .get_property_inner()
369 .expect("OnEditor field of non-nullable type must be initialized before access")
370 }
371
372 fn var_set(field: &mut Self, value: Self::Via) {
373 field.set_property_inner(Some(value));
374 }
375
376 fn var_pub_get(field: &Self) -> Self::PubType {
377 Self::var_get(field)
378 }
379
380 fn var_pub_set(field: &mut Self, value: Self::PubType) {
381 Self::var_set(field, value)
382 }
383}
384
385impl<T> Export for OnEditor<T>
386where
387 OnEditor<T>: Var,
388 T: GodotConvert + Export,
389 T::Via: BuiltinExport,
390{
391}
392
393impl<T> BuiltinExport for OnEditor<T> {}