12
12
13
13
use crate :: context:: Context ;
14
14
use crate :: conv;
15
- use crate :: models:: domain:: { Class , ClassLike , ClassSignal , FnParam , RustTy , TyName } ;
15
+ use crate :: models:: domain:: { Class , ClassLike , ClassSignal , FnParam , ModName , RustTy , TyName } ;
16
16
use crate :: util:: { ident, safe_ident} ;
17
17
use proc_macro2:: { Ident , TokenStream } ;
18
18
use quote:: { format_ident, quote} ;
19
19
20
+ pub struct SignalCodegen {
21
+ pub signal_code : TokenStream ,
22
+ pub has_own_signals : bool ,
23
+ }
24
+
20
25
pub fn make_class_signals (
21
26
class : & Class ,
22
27
signals : & [ ClassSignal ] ,
23
- _ctx : & mut Context ,
24
- ) -> Option < TokenStream > {
25
- if signals. is_empty ( ) {
26
- return None ;
27
- }
28
-
28
+ ctx : & mut Context ,
29
+ ) -> SignalCodegen {
29
30
let all_params: Vec < SignalParams > = signals
30
31
. iter ( )
31
32
. map ( |s| SignalParams :: new ( & s. parameters ) )
32
33
. collect ( ) ;
33
34
34
- let signal_collection_struct = make_signal_collection ( class, signals, & all_params) ;
35
+ let class_name = class. name ( ) ;
36
+
37
+ // If no signals are defined in current class, walk up until we find some.
38
+ let ( own_collection_struct, nearest_collection_name, nearest_class, has_own_signals) ;
39
+ if signals. is_empty ( ) {
40
+ // Use the nearest base class that *has* signals, and store its collection name.
41
+ let nearest = ctx. find_nearest_base_with_signals ( class_name) ;
42
+
43
+ // Doesn't define own collection struct if no signals are present (note that WithSignals is still implemented).
44
+ own_collection_struct = TokenStream :: new ( ) ;
45
+ nearest_collection_name = make_collection_name ( & nearest) ;
46
+ nearest_class = Some ( nearest) ;
47
+ has_own_signals = false ;
48
+ } else {
49
+ let ( code, name) = make_signal_collection ( class, signals, & all_params) ;
50
+
51
+ own_collection_struct = code;
52
+ nearest_collection_name = name;
53
+ nearest_class = None ;
54
+ has_own_signals = true ;
55
+ } ;
35
56
36
57
let signal_types = signals
37
58
. iter ( )
38
59
. zip ( all_params. iter ( ) )
39
60
. map ( |( signal, params) | make_signal_individual_struct ( signal, params) ) ;
40
61
41
- let class_name = class. name ( ) ;
62
+ let with_signals_impl =
63
+ make_with_signals_impl ( class_name, & nearest_collection_name, nearest_class. as_ref ( ) ) ;
42
64
43
- Some ( quote ! {
65
+ let deref_impl =
66
+ has_own_signals. then ( || make_upcast_deref_impl ( class_name, & nearest_collection_name) ) ;
67
+
68
+ let code = quote ! {
44
69
#[ cfg( since_api = "4.2" ) ]
45
70
pub use signals:: * ;
46
71
47
72
#[ cfg( since_api = "4.2" ) ]
48
73
mod signals {
49
- use crate :: obj:: Gd ;
74
+ use crate :: obj:: { Gd , GodotClass } ;
50
75
use super :: re_export:: #class_name;
76
+ use crate :: registry:: signal:: TypedSignal ;
51
77
use super :: * ;
52
78
53
- #signal_collection_struct
79
+ // These may be empty if the class doesn't define any signals itself.
80
+ #own_collection_struct
54
81
#( #signal_types ) *
82
+
83
+ // These are always present.
84
+ #with_signals_impl
85
+ #deref_impl
55
86
}
56
- } )
87
+ } ;
88
+
89
+ SignalCodegen {
90
+ signal_code : code,
91
+ has_own_signals,
92
+ }
93
+ }
94
+
95
+ /// Creates `impl WithSignals`.
96
+ ///
97
+ /// Present for every single class, as every class has at least inherited signals (since `Object` has some).
98
+ fn make_with_signals_impl (
99
+ class_name : & TyName ,
100
+ collection_struct_name : & Ident ,
101
+ nearest_class : Option < & TyName > , // None if own class has signals.
102
+ ) -> TokenStream {
103
+ let base_use_statement = quote ! { use crate :: obj:: WithSignals ; } ;
104
+ let use_statement = if let Some ( nearest_class) = nearest_class {
105
+ let module_name = ModName :: from_godot ( & nearest_class. godot_ty ) ;
106
+ quote ! {
107
+ #base_use_statement
108
+ use crate :: classes:: #module_name:: #collection_struct_name;
109
+ }
110
+ } else {
111
+ base_use_statement
112
+ } ;
113
+
114
+ quote ! {
115
+ #use_statement
116
+ impl WithSignals for #class_name {
117
+ type SignalCollection <' c, C : WithSignals > = #collection_struct_name<' c, C >;
118
+ type __SignalObj<' c> = Gd <Self >;
119
+ // type __SignalObj<'c, C: WithSignals> = Gd<Self>;
120
+
121
+ // During construction, C = Self.
122
+ #[ doc( hidden) ]
123
+ fn __signals_from_external( gd_mut: & mut Gd <Self >) -> Self :: SignalCollection <' _, Self > {
124
+ Self :: SignalCollection {
125
+ __internal_obj: Some ( gd_mut. clone( ) ) ,
126
+ }
127
+ }
128
+ }
129
+ }
57
130
}
58
131
59
132
// Used outside, to document class with links to this type.
@@ -70,7 +143,9 @@ fn make_signal_collection(
70
143
class : & Class ,
71
144
signals : & [ ClassSignal ] ,
72
145
params : & [ SignalParams ] ,
73
- ) -> TokenStream {
146
+ ) -> ( TokenStream , Ident ) {
147
+ debug_assert ! ( !signals. is_empty( ) ) ; // checked outside
148
+
74
149
let class_name = class. name ( ) ;
75
150
let collection_struct_name = make_collection_name ( class_name) ;
76
151
@@ -83,9 +158,9 @@ fn make_signal_collection(
83
158
quote ! {
84
159
// Important to return lifetime 'c here, not '_.
85
160
#[ doc = #provider_docs]
86
- pub fn #signal_name( & mut self ) -> #individual_struct_name<' c> {
161
+ pub fn #signal_name( & mut self ) -> #individual_struct_name<' c, C > {
87
162
#individual_struct_name {
88
- typed: crate :: registry :: signal :: TypedSignal :: new ( self . __gd . clone ( ) , #signal_name_str)
163
+ typed: TypedSignal :: extract ( & mut self . __internal_obj , #signal_name_str)
89
164
}
90
165
}
91
166
}
@@ -96,26 +171,48 @@ fn make_signal_collection(
96
171
c = class_name. rust_ty
97
172
) ;
98
173
99
- quote ! {
174
+ let code = quote ! {
100
175
#[ doc = #collection_docs]
101
- pub struct #collection_struct_name<' c> {
102
- __gd: & ' c mut Gd <#class_name>,
176
+ // C is needed for signals of derived classes that are upcast via Deref; C in that class is the derived class.
177
+ pub struct #collection_struct_name<' c, C : WithSignals = #class_name>
178
+ {
179
+ #[ doc( hidden) ]
180
+ pub ( crate ) __internal_obj: Option <C :: __SignalObj<' c>>,
103
181
}
104
182
105
- impl <' c> #collection_struct_name<' c> {
183
+ impl <' c, C : WithSignals > #collection_struct_name<' c, C > {
106
184
#( #provider_methods ) *
107
185
}
186
+ } ;
108
187
109
- impl crate :: obj:: WithSignals for #class_name {
110
- type SignalCollection <' c> = #collection_struct_name<' c>;
111
- #[ doc( hidden) ]
112
- type __SignalObject<' c> = Gd <#class_name>;
188
+ ( code, collection_struct_name)
189
+ }
113
190
114
- #[ doc( hidden) ]
115
- fn __signals_from_external( external: & mut Gd <Self >) -> Self :: SignalCollection <' _> {
116
- Self :: SignalCollection {
117
- __gd: external,
118
- }
191
+ fn make_upcast_deref_impl ( class_name : & TyName , collection_struct_name : & Ident ) -> TokenStream {
192
+ // Root of hierarchy, no "upcast" derefs.
193
+ if class_name. rust_ty == "Object" {
194
+ return TokenStream :: new ( ) ;
195
+ }
196
+
197
+ quote ! {
198
+ impl <' c, C : WithSignals > std:: ops:: Deref for #collection_struct_name<' c, C > {
199
+ // The whole upcast mechanism is based on C remaining the same even through upcast.
200
+ type Target = <
201
+ <
202
+ #class_name as crate :: obj:: GodotClass
203
+ >:: Base as WithSignals
204
+ >:: SignalCollection <' c, C >;
205
+
206
+ fn deref( & self ) -> & Self :: Target {
207
+ type Derived = #class_name;
208
+ crate :: private:: signal_collection_to_base:: <C , Derived >( self )
209
+ }
210
+ }
211
+
212
+ impl <' c, C : WithSignals > std:: ops:: DerefMut for #collection_struct_name<' c, C > {
213
+ fn deref_mut( & mut self ) -> & mut Self :: Target {
214
+ type Derived = #class_name;
215
+ crate :: private:: signal_collection_to_base_mut:: <C , Derived >( self )
119
216
}
120
217
}
121
218
}
@@ -139,10 +236,10 @@ fn make_signal_individual_struct(signal: &ClassSignal, params: &SignalParams) ->
139
236
// Embedded in `mod signals`.
140
237
quote ! {
141
238
// Reduce tokens to parse by reusing this type definitions.
142
- type #typed_name<' c> = crate :: registry :: signal :: TypedSignal <' c, #class_ty , #param_tuple>;
239
+ type #typed_name<' c, C > = TypedSignal <' c, C , #param_tuple>;
143
240
144
- pub struct #individual_struct_name<' c> {
145
- typed: #typed_name<' c>,
241
+ pub struct #individual_struct_name<' c, C : WithSignals = #class_ty > {
242
+ typed: #typed_name<' c, C >,
146
243
}
147
244
148
245
impl <' c> #individual_struct_name<' c> {
@@ -151,15 +248,15 @@ fn make_signal_individual_struct(signal: &ClassSignal, params: &SignalParams) ->
151
248
}
152
249
}
153
250
154
- impl <' c> std:: ops:: Deref for #individual_struct_name<' c> {
155
- type Target = #typed_name<' c>;
251
+ impl <' c, C : WithSignals > std:: ops:: Deref for #individual_struct_name<' c, C > {
252
+ type Target = #typed_name<' c, C >;
156
253
157
254
fn deref( & self ) -> & Self :: Target {
158
255
& self . typed
159
256
}
160
257
}
161
258
162
- impl std:: ops:: DerefMut for #individual_struct_name<' _> {
259
+ impl < C : WithSignals > std:: ops:: DerefMut for #individual_struct_name<' _, C > {
163
260
fn deref_mut( & mut self ) -> & mut Self :: Target {
164
261
& mut self . typed
165
262
}
0 commit comments