@@ -211,7 +211,7 @@ pub fn make_signal_registrations(
211
211
}
212
212
213
213
#[ cfg( since_api = "4.2" ) ]
214
- let signal_symbols = make_signal_symbols ( class_name, collection_api) ;
214
+ let signal_symbols = Some ( make_signal_symbols ( class_name, collection_api) ) ;
215
215
#[ cfg( before_api = "4.2" ) ]
216
216
let signal_symbols = None ;
217
217
@@ -311,7 +311,7 @@ impl SignalCollection {
311
311
. push ( make_signal_individual_struct ( details) )
312
312
}
313
313
314
- pub fn is_empty ( & self ) -> bool {
314
+ fn is_empty ( & self ) -> bool {
315
315
self . individual_structs . is_empty ( )
316
316
}
317
317
}
@@ -380,18 +380,33 @@ fn make_signal_individual_struct(details: &SignalDetails) -> TokenStream {
380
380
381
381
/// Generates symbolic API for signals:
382
382
/// * collection (unspecified-name struct holding methods to access each signal)
383
+ /// * can be absent if the class declares no own #[signal]s.
383
384
/// * individual signal types
384
385
/// * trait impls
385
386
fn make_signal_symbols (
386
387
class_name : & Ident ,
387
388
collection_api : SignalCollection ,
388
389
// max_visibility: SignalVisibility,
389
- ) -> Option < TokenStream > {
390
- // Future note: if we add Rust->Rust inheritance, then the WithSignals trait must be unconditionally implemented .
390
+ ) -> TokenStream {
391
+ // If class declares no own signals, just implement the traits, no collection APIs .
391
392
if collection_api. is_empty ( ) {
392
- return None ;
393
+ let with_signals_impl = make_with_signals_impl_delegated_to_base ( class_name) ;
394
+ let base_field_macro = util:: format_class_base_field_macro ( class_name) ;
395
+
396
+ // base_field_macro! is a macro that expands to all input tokens if the class declares a Base<T> field, and to nothing otherwise.
397
+ // This makes sure that WithSignals is only implemented for classes with a base field, and avoids compile errors about it.
398
+ // Note: this doesn't work with `impl nested::MyClass` style remote impls, which can be enabled via #[hint(signals=true/false)].
399
+
400
+ return quote ! {
401
+ #base_field_macro! {
402
+ #with_signals_impl
403
+ }
404
+ } ;
393
405
}
394
406
407
+ // For the regular flow, we *expect* that a Base<T> field is present. Having a concise error message "missing Base<T> field" is
408
+ // better than magic tricks, since the user explicitly asked for #[signal] and thus should be guided to complete the missing parts.
409
+
395
410
let collection_struct_name = format_ident ! ( "__godot_Signals_{}" , class_name) ;
396
411
let collection_struct_methods = & collection_api. provider_methods ;
397
412
let with_signals_impl = make_with_signals_impl ( class_name, & collection_struct_name) ;
@@ -455,9 +470,10 @@ fn make_signal_symbols(
455
470
#( #individual_structs ) *
456
471
} ;
457
472
458
- Some ( code)
473
+ code
459
474
}
460
475
476
+ /// Declare `impl WithSignals` and `impl WithUserSignals` with own signal collection.
461
477
fn make_with_signals_impl ( class_name : & Ident , collection_struct_name : & Ident ) -> TokenStream {
462
478
quote ! {
463
479
impl :: godot:: obj:: WithSignals for #class_name {
@@ -467,10 +483,10 @@ fn make_with_signals_impl(class_name: &Ident, collection_struct_name: &Ident) ->
467
483
type __SignalObj<' c> = :: godot:: private:: UserSignalObject <' c, Self >;
468
484
469
485
#[ doc( hidden) ]
470
- fn __signals_from_external( external: & mut Gd <Self >) -> Self :: SignalCollection <' _, Self > {
486
+ fn __signals_from_external( external: & mut :: godot :: obj :: Gd <Self >) -> Self :: SignalCollection <' _, Self > {
471
487
Self :: SignalCollection {
472
488
__internal_obj: Some ( :: godot:: private:: UserSignalObject :: External {
473
- gd: external. clone( ) . upcast :: < Object > ( )
489
+ gd: external. clone( ) . upcast_object ( )
474
490
} )
475
491
}
476
492
}
@@ -486,6 +502,39 @@ fn make_with_signals_impl(class_name: &Ident, collection_struct_name: &Ident) ->
486
502
}
487
503
}
488
504
505
+ /// Declare `impl WithSignals` and `impl WithUserSignals`, but without own signal collection; instead delegate to base collection.
506
+ fn make_with_signals_impl_delegated_to_base ( class_name : & Ident ) -> TokenStream {
507
+ quote ! {
508
+ impl :: godot:: obj:: WithSignals for #class_name {
509
+ type SignalCollection <' c, C : :: godot:: obj:: WithSignals > =
510
+ <<Self as :: godot:: obj:: GodotClass >:: Base as :: godot:: obj:: WithSignals >:: SignalCollection <' c, C >;
511
+
512
+ #[ doc( hidden) ]
513
+ type __SignalObj<' c> =
514
+ <<Self as :: godot:: obj:: GodotClass >:: Base as :: godot:: obj:: WithSignals >:: __SignalObj<' c>;
515
+
516
+
517
+ #[ doc( hidden) ]
518
+ fn __signals_from_external( external: & mut :: godot:: obj:: Gd <Self >) -> Self :: SignalCollection <' _, Self > {
519
+ // This will need updating when allowing Rust->Rust inheritance.
520
+ Self :: SignalCollection {
521
+ __internal_obj: Some ( external. clone( ) . upcast( ) )
522
+ }
523
+ }
524
+ }
525
+
526
+ impl :: godot:: obj:: WithUserSignals for #class_name {
527
+ fn signals( & mut self ) -> Self :: SignalCollection <' _, Self > {
528
+ // This will need updating when allowing Rust->Rust inheritance.
529
+ let self_gd = <Self as :: godot:: obj:: WithBaseField >:: to_gd( self ) ;
530
+ Self :: SignalCollection {
531
+ __internal_obj: Some ( self_gd. upcast( ) ) ,
532
+ }
533
+ }
534
+ }
535
+ }
536
+ }
537
+
489
538
fn make_upcast_deref_impl ( class_name : & Ident , collection_struct_name : & Ident ) -> TokenStream {
490
539
quote ! {
491
540
impl <' c, C : :: godot:: obj:: WithSignals > std:: ops:: Deref for #collection_struct_name<' c, C > {
0 commit comments