@@ -391,34 +391,23 @@ fn make_signal_symbols(
391
391
// max_visibility: SignalVisibility,
392
392
hints : GodotApiHints ,
393
393
) -> TokenStream {
394
- // If class declares no own signals, just implement the traits, no collection APIs.
395
- if hints. has_typed_signals == Some ( false ) || collection_api. is_empty ( ) {
396
- let with_signals_impl = make_with_signals_impl_delegated_to_base ( class_name) ;
397
-
398
- // base_field_macro! is a macro that expands to all input tokens if the class declares a Base<T> field, and to nothing otherwise.
399
- // This makes sure that WithSignals is only implemented for classes with a base field, and avoids compile errors about it.
400
-
401
- return match hints. has_typed_signals {
402
- // The default case: try to infer from #[derive(GodotClass)] declaration whether a Base<T> field is present.
403
- None => {
404
- let base_field_macro = util:: format_class_base_field_macro ( class_name) ;
405
- quote ! {
406
- #base_field_macro! {
407
- #with_signals_impl
408
- }
409
- }
410
- }
411
-
412
- // Hints are useful in cases like `impl nested::MyClass` style remote impls. Here, the decl-macro can't work due to being invisible
413
- // in other scopes (and crate-global #[macro_export] has its own problems). Thus, generate code directly without decl-macro.
414
- Some ( true ) => quote ! { #with_signals_impl } ,
415
- Some ( false ) => quote ! { } ,
416
- } ;
394
+ // Return early if typed signals are explicitly disabled.
395
+ if hints. has_typed_signals == Some ( false ) {
396
+ return TokenStream :: new ( ) ;
417
397
}
418
398
419
- // For the regular flow, we *expect* that a Base<T> field is present. Having a concise error message "missing Base<T> field" is
420
- // better than magic tricks, since the user explicitly asked for #[signal] and thus should be guided to complete the missing parts.
399
+ // Earlier implementation generated a simplified code when no #[signal] was declared: only WithSignals/WithUserSignals impl, but no own
400
+ // collection, instead the associated type pointing to the base class. This has however some problems:
401
+ // * Part of the reason for user-defined collection is to store UserSignalObject instead of Gd, which can store &mut self.
402
+ // This is necessary for self.signals().some_base_signal().emit(), if such a signal is connected to Self::method_mut;
403
+ // Gd would cause a borrow error.
404
+ // * Once we add Rust-Rust inheritance, we'd need to differentiate case again, which can be tricky since #[godot_api] has no information
405
+ // about the base class.
406
+ //
407
+ // As compromise, we always generate a collection struct if either at least 1 #[signal] is declared or the struct has a Base<T> field.
408
+ // We also provide opt-out via #[godot_api(no_typed_signals)].
421
409
410
+ let declares_no_signals = collection_api. is_empty ( ) ;
422
411
let collection_struct_name = format_ident ! ( "__godot_Signals_{}" , class_name) ;
423
412
let collection_struct_methods = & collection_api. provider_methods ;
424
413
let with_signals_impl = make_with_signals_impl ( class_name, & collection_struct_name) ;
@@ -458,7 +447,7 @@ fn make_signal_symbols(
458
447
459
448
let visibility_macro = util:: format_class_visibility_macro ( class_name) ;
460
449
461
- let code = quote ! {
450
+ let mut code = quote ! {
462
451
#visibility_macro! {
463
452
#[ allow( non_camel_case_types) ]
464
453
#[ doc( hidden) ] // Only on struct, not methods, to allow completion in IDEs.
@@ -482,6 +471,17 @@ fn make_signal_symbols(
482
471
#( #individual_structs ) *
483
472
} ;
484
473
474
+ // base_field_macro! is a macro that expands to all input tokens if the class declares a Base<T> field, and to nothing otherwise.
475
+ // This makes sure that WithSignals is only implemented for classes with a base field, and avoids compile errors about it.
476
+ // Only when no #[signal] is declared -> otherwise the user explicitly requests it, and a compile error is better to guide them.
477
+ if declares_no_signals {
478
+ let base_field_macro = util:: format_class_base_field_macro ( class_name) ;
479
+
480
+ code = quote ! {
481
+ #base_field_macro! { #code }
482
+ } ;
483
+ }
484
+
485
485
code
486
486
}
487
487
@@ -514,39 +514,6 @@ fn make_with_signals_impl(class_name: &Ident, collection_struct_name: &Ident) ->
514
514
}
515
515
}
516
516
517
- /// Declare `impl WithSignals` and `impl WithUserSignals`, but without own signal collection; instead delegate to base collection.
518
- fn make_with_signals_impl_delegated_to_base ( class_name : & Ident ) -> TokenStream {
519
- quote ! {
520
- impl :: godot:: obj:: WithSignals for #class_name {
521
- type SignalCollection <' c, C : :: godot:: obj:: WithSignals > =
522
- <<Self as :: godot:: obj:: GodotClass >:: Base as :: godot:: obj:: WithSignals >:: SignalCollection <' c, C >;
523
-
524
- #[ doc( hidden) ]
525
- type __SignalObj<' c> =
526
- <<Self as :: godot:: obj:: GodotClass >:: Base as :: godot:: obj:: WithSignals >:: __SignalObj<' c>;
527
-
528
-
529
- #[ doc( hidden) ]
530
- fn __signals_from_external( external: & mut :: godot:: obj:: Gd <Self >) -> Self :: SignalCollection <' _, Self > {
531
- // This will need updating when allowing Rust->Rust inheritance.
532
- Self :: SignalCollection {
533
- __internal_obj: Some ( external. clone( ) . upcast( ) )
534
- }
535
- }
536
- }
537
-
538
- impl :: godot:: obj:: WithUserSignals for #class_name {
539
- fn signals( & mut self ) -> Self :: SignalCollection <' _, Self > {
540
- // This will need updating when allowing Rust->Rust inheritance.
541
- let self_gd = <Self as :: godot:: obj:: WithBaseField >:: to_gd( self ) ;
542
- Self :: SignalCollection {
543
- __internal_obj: Some ( self_gd. upcast( ) ) ,
544
- }
545
- }
546
- }
547
- }
548
- }
549
-
550
517
fn make_upcast_deref_impl ( class_name : & Ident , collection_struct_name : & Ident ) -> TokenStream {
551
518
quote ! {
552
519
impl <' c, C : :: godot:: obj:: WithSignals > std:: ops:: Deref for #collection_struct_name<' c, C > {
0 commit comments