55 * file, You can obtain one at https://mozilla.org/MPL/2.0/.
66 */
77
8+ use crate :: builtin:: NodePath ;
9+ use crate :: classes:: Node ;
810use crate :: meta:: GodotConvert ;
11+ use crate :: obj:: { Gd , GodotClass , Inherits } ;
912use crate :: registry:: property:: { PropertyHintInfo , Var } ;
1013use std:: mem;
1114
@@ -17,9 +20,10 @@ use std::mem;
1720///
1821/// `OnReady<T>` should always be used as a field. There are two modes to use it:
1922///
20- /// 1. **Automatic mode, using [`new()`](Self::new).**<br>
21- /// Before `ready()` is called, all `OnReady` fields constructed with `new()` are automatically initialized, in the order of
22- /// declaration. This means that you can safely access them in `ready()`.<br><br>
23+ /// 1. **Automatic mode, using [`new()`](OnReady::new), [`from_base_fn()`](OnReady::from_base_fn) or
24+ /// [`node()`](OnReady::<Gd<T>>::node).**<br>
25+ /// Before `ready()` is called, all `OnReady` fields constructed with the above methods are automatically initialized,
26+ /// in the order of declaration. This means that you can safely access them in `ready()`.<br><br>
2327/// 2. **Manual mode, using [`manual()`](Self::manual).**<br>
2428/// These fields are left uninitialized until you call [`init()`][Self::init] on them. This is useful if you need more complex
2529/// initialization scenarios than a closure allows. If you forget initialization, a panic will occur on first access.
@@ -36,21 +40,27 @@ use std::mem;
3640/// [option]: std::option::Option
3741/// [lazy]: https://docs.rs/once_cell/1/once_cell/unsync/struct.Lazy.html
3842///
39- /// # Example
43+ /// # Requirements
44+ /// - The class must have an explicit `Base` field (i.e. `base: Base<Node>`).
45+ /// - The class must inherit `Node` (otherwise `ready()` would not exist anyway).
46+ ///
47+ /// # Example - user-defined `init`
4048/// ```
4149/// use godot::prelude::*;
4250///
4351/// #[derive(GodotClass)]
4452/// #[class(base = Node)]
4553/// struct MyClass {
54+ /// base: Base<Node>,
4655/// auto: OnReady<i32>,
4756/// manual: OnReady<i32>,
4857/// }
4958///
5059/// #[godot_api]
5160/// impl INode for MyClass {
52- /// fn init(_base : Base<Node>) -> Self {
61+ /// fn init(base : Base<Node>) -> Self {
5362/// Self {
63+ /// base,
5464/// auto: OnReady::new(|| 11),
5565/// manual: OnReady::manual(),
5666/// }
@@ -65,10 +75,54 @@ use std::mem;
6575/// assert_eq!(*self.manual, 22);
6676/// }
6777/// }
78+ /// ```
79+ ///
80+ /// # Example - macro-generated `init`
81+ /// ```
82+ /// use godot::prelude::*;
83+ ///
84+ /// #[derive(GodotClass)]
85+ /// #[class(init, base = Node)]
86+ /// struct MyClass {
87+ /// base: Base<Node>,
88+ /// #[init(node = "ChildPath")]
89+ /// auto: OnReady<Gd<Node2D>>,
90+ /// #[init(default = OnReady::manual())]
91+ /// manual: OnReady<i32>,
92+ /// }
93+ ///
94+ /// #[godot_api]
95+ /// impl INode for MyClass {
96+ /// fn ready(&mut self) {
97+ /// // self.node is now ready with the node found at path `ChildPath`.
98+ /// assert_eq!(self.auto.get_name(), "ChildPath".into());
99+ ///
100+ /// // self.manual needs to be initialized manually.
101+ /// self.manual.init(22);
102+ /// assert_eq!(*self.manual, 22);
103+ /// }
104+ /// }
105+ /// ```
68106pub struct OnReady < T > {
69107 state : InitState < T > ,
70108}
71109
110+ impl < T : GodotClass + Inherits < Node > > OnReady < Gd < T > > {
111+ /// Variant of [`OnReady::new()`], fetching the node located at `path` before `ready()`.
112+ ///
113+ /// This is the functional equivalent of the GDScript pattern `@onready var node = $NodePath`.
114+ ///
115+ /// # Panics
116+ /// - If `path` does not point to a valid node.
117+ ///
118+ /// Note that the panic will only happen if and when the node enters the SceneTree for the first time
119+ /// (i.e.: it receives the `READY` notification).
120+ pub fn node ( path : impl Into < NodePath > ) -> Self {
121+ let path = path. into ( ) ;
122+ Self :: from_base_fn ( |base| base. get_node_as ( path) )
123+ }
124+ }
125+
72126impl < T > OnReady < T > {
73127 /// Schedule automatic initialization before `ready()`.
74128 ///
@@ -82,6 +136,14 @@ impl<T> OnReady<T> {
82136 pub fn new < F > ( init_fn : F ) -> Self
83137 where
84138 F : FnOnce ( ) -> T + ' static ,
139+ {
140+ Self :: from_base_fn ( |_| init_fn ( ) )
141+ }
142+
143+ /// Variant of [`OnReady::new()`], allowing access to `Base` when initializing.
144+ pub fn from_base_fn < F > ( init_fn : F ) -> Self
145+ where
146+ F : FnOnce ( & Gd < Node > ) -> T + ' static ,
85147 {
86148 Self {
87149 state : InitState :: AutoPrepared {
@@ -126,7 +188,7 @@ impl<T> OnReady<T> {
126188 ///
127189 /// # Panics
128190 /// If the value is already initialized.
129- pub ( crate ) fn init_auto ( & mut self ) {
191+ pub ( crate ) fn init_auto ( & mut self , base : & Gd < Node > ) {
130192 // Two branches needed, because mem::replace() could accidentally overwrite an already initialized value.
131193 match & self . state {
132194 InitState :: ManualUninitialized => return , // skipped
@@ -147,7 +209,7 @@ impl<T> OnReady<T> {
147209 } ;
148210
149211 self . state = InitState :: Initialized {
150- value : initializer ( ) ,
212+ value : initializer ( base ) ,
151213 } ;
152214 }
153215}
@@ -214,9 +276,11 @@ impl<T: Var> Var for OnReady<T> {
214276// ----------------------------------------------------------------------------------------------------------------------------------------------
215277// Implementation
216278
279+ type InitFn < T > = dyn FnOnce ( & Gd < Node > ) -> T ;
280+
217281enum InitState < T > {
218282 ManualUninitialized ,
219- AutoPrepared { initializer : Box < dyn FnOnce ( ) -> T > } ,
283+ AutoPrepared { initializer : Box < InitFn < T > > } ,
220284 AutoInitializing , // needed because state cannot be empty
221285 Initialized { value : T } ,
222286}
0 commit comments