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