@@ -64,58 +64,55 @@ impl Callable {
64
64
}
65
65
}
66
66
67
+ /// Create a callable for a method on any [`Variant`].
68
+ ///
69
+ /// Allows to dynamically call methods on builtin types (e.g. `String.md5_text`). Note that Godot method names are used, not Rust ones.
70
+ /// If the variant type is `Object`, the behavior will match that of `from_object_method()`.
71
+ ///
72
+ /// If the builtin type does not have the method, the returned callable will be invalid.
73
+ ///
74
+ /// Static builtin methods (e.g. `String.humanize_size`) are not supported in reflection as of Godot 4.4. For static _class_ functions,
75
+ /// use [`from_local_static()`][Self::from_local_static] instead.
76
+ ///
77
+ /// _Godot equivalent: `Callable.create(Variant variant, StringName method)`_
78
+ #[ cfg( since_api = "4.3" ) ]
79
+ pub fn from_variant_method < S > ( variant : & Variant , method_name : S ) -> Self
80
+ where
81
+ S : meta:: AsArg < StringName > ,
82
+ {
83
+ meta:: arg_into_ref!( method_name) ;
84
+ inner:: InnerCallable :: create ( variant, method_name)
85
+ }
86
+
67
87
/// Create a callable for the static method `class_name::function` (single-threaded).
68
88
///
69
89
/// Allows you to call static functions through `Callable`.
70
90
///
71
- /// Note that due to varying support across different engine versions, the resulting `Callable` has unspecified behavior for
72
- /// methods such as [`method_name()`][Self::method_name], [`object()`][Self::object], [`object_id()`][Self::object_id] or
73
- /// [`get_argument_count()`][Self::arg_len] among others. It is recommended to only use this for calling the function.
91
+ /// Does not support built-in types (such as `String`), only classes.
74
92
///
75
93
/// # Compatibility
76
- /// Up until and including Godot 4.3, this method has some limitations:
77
- /// - [`is_valid()`][Self::is_valid] will return `false`, even though the call itself succeeds .
78
- /// - You cannot use statics to connect signals to such callables. Use the new typed signal API instead.
94
+ /// Not available before Godot 4.4. Library versions <0.3 used to provide this, however the polyfill used to emulate it was half-broken
95
+ /// (not supporting signals, bind(), method_name(), is_valid(), etc) .
96
+ # [ cfg ( since_api = "4.4" ) ]
79
97
pub fn from_local_static (
80
98
class_name : impl meta:: AsArg < StringName > ,
81
99
function_name : impl meta:: AsArg < StringName > ,
82
100
) -> Self {
83
101
meta:: arg_into_owned!( class_name) ;
84
102
meta:: arg_into_owned!( function_name) ;
85
103
86
- // Modern implementation: use ClassDb::class_call_static().
87
- #[ cfg( since_api = "4.4" ) ]
88
- {
89
- let callable_name = format ! ( "{class_name}.{function_name}" ) ;
90
-
91
- Self :: from_local_fn ( & callable_name, move |args| {
92
- let args = args. iter ( ) . cloned ( ) . cloned ( ) . collect :: < Vec < _ > > ( ) ;
104
+ let callable_name = format ! ( "{class_name}.{function_name}" ) ;
93
105
94
- let result: Variant = classes:: ClassDb :: singleton ( ) . class_call_static (
95
- & class_name,
96
- & function_name,
97
- args. as_slice ( ) ,
98
- ) ;
99
- Ok ( result)
100
- } )
101
- }
106
+ Self :: from_local_fn ( & callable_name, move |args| {
107
+ let args = args. iter ( ) . cloned ( ) . cloned ( ) . collect :: < Vec < _ > > ( ) ;
102
108
103
- // Polyfill for <= Godot 4.3: use GDScript expressions.
104
- #[ cfg( before_api = "4.4" ) ]
105
- {
106
- use crate :: obj:: NewGd ;
107
-
108
- let code = format ! (
109
- "static func __callable():\n \t return Callable({class_name}, \" {function_name}\" )"
109
+ let result: Variant = classes:: ClassDb :: singleton ( ) . class_call_static (
110
+ & class_name,
111
+ & function_name,
112
+ args. as_slice ( ) ,
110
113
) ;
111
-
112
- let mut script = classes:: GDScript :: new_gd ( ) ;
113
- script. set_source_code ( & code) ;
114
- script. reload ( ) ;
115
-
116
- let callable = script. call ( "__callable" , & [ ] ) ;
117
- callable. to ( )
118
- }
114
+ Ok ( result)
115
+ } )
119
116
}
120
117
121
118
#[ cfg( since_api = "4.2" ) ]
@@ -125,7 +122,7 @@ impl Callable {
125
122
token : ptr:: null_mut ( ) ,
126
123
object_id : 0 ,
127
124
call_func : None ,
128
- is_valid_func : None , // could be customized, but no real use case yet .
125
+ is_valid_func : None , // overwritten later .
129
126
free_func : None ,
130
127
hash_func : None ,
131
128
equal_func : None ,
@@ -158,6 +155,24 @@ impl Callable {
158
155
} )
159
156
}
160
157
158
+ #[ cfg( since_api = "4.2" ) ]
159
+ pub ( crate ) fn with_scoped_fn < S , F , Fc , R > ( name : S , rust_function : F , callable_usage : Fc ) -> R
160
+ where
161
+ S : meta:: AsArg < GString > ,
162
+ F : FnMut ( & [ & Variant ] ) -> Result < Variant , ( ) > ,
163
+ Fc : FnOnce ( & Callable ) -> R ,
164
+ {
165
+ meta:: arg_into_owned!( name) ;
166
+
167
+ let callable = Self :: from_fn_wrapper ( FnWrapper {
168
+ rust_function,
169
+ name,
170
+ thread_id : Some ( std:: thread:: current ( ) . id ( ) ) ,
171
+ } ) ;
172
+
173
+ callable_usage ( & callable)
174
+ }
175
+
161
176
/// Create callable from **thread-safe** Rust function or closure.
162
177
///
163
178
/// `name` is used for the string representation of the closure, which helps debugging.
@@ -219,12 +234,14 @@ impl Callable {
219
234
let userdata = CallableUserdata { inner : callable } ;
220
235
221
236
let info = CallableCustomInfo {
237
+ // We could technically associate an object_id with the custom callable. is_valid_func would then check that for validity.
222
238
callable_userdata : Box :: into_raw ( Box :: new ( userdata) ) as * mut std:: ffi:: c_void ,
223
239
call_func : Some ( rust_callable_call_custom :: < C > ) ,
224
240
free_func : Some ( rust_callable_destroy :: < C > ) ,
225
241
hash_func : Some ( rust_callable_hash :: < C > ) ,
226
242
equal_func : Some ( rust_callable_equal :: < C > ) ,
227
243
to_string_func : Some ( rust_callable_to_string_display :: < C > ) ,
244
+ is_valid_func : Some ( rust_callable_is_valid_custom :: < C > ) ,
228
245
..Self :: default_callable_custom_info ( )
229
246
} ;
230
247
@@ -243,6 +260,7 @@ impl Callable {
243
260
call_func : Some ( rust_callable_call_fn :: < F > ) ,
244
261
free_func : Some ( rust_callable_destroy :: < FnWrapper < F > > ) ,
245
262
to_string_func : Some ( rust_callable_to_string_named :: < F > ) ,
263
+ is_valid_func : Some ( rust_callable_is_valid) ,
246
264
..Self :: default_callable_custom_info ( )
247
265
} ;
248
266
@@ -530,6 +548,17 @@ mod custom_callable {
530
548
/// Error handling is mostly needed in case argument number or types mismatch.
531
549
#[ allow( clippy:: result_unit_err) ] // TODO remove once there's a clear error type here.
532
550
fn invoke ( & mut self , args : & [ & Variant ] ) -> Result < Variant , ( ) > ;
551
+
552
+ // TODO(v0.3): add object_id().
553
+
554
+ /// Returns whether the callable is considered valid.
555
+ ///
556
+ /// True by default.
557
+ ///
558
+ /// If this Callable stores an object, this method should return whether that object is alive.
559
+ fn is_valid ( & self ) -> bool {
560
+ true
561
+ }
533
562
}
534
563
535
564
pub unsafe extern "C" fn rust_callable_call_custom < C : RustCallable > (
@@ -641,4 +670,23 @@ mod custom_callable {
641
670
w. name . clone ( ) . move_into_string_ptr ( r_out) ;
642
671
* r_is_valid = sys:: conv:: SYS_TRUE ;
643
672
}
673
+
674
+ // Implementing this is necessary because the default (nullptr) may consider custom callables as invalid in some cases.
675
+ pub unsafe extern "C" fn rust_callable_is_valid_custom < C : RustCallable > (
676
+ callable_userdata : * mut std:: ffi:: c_void ,
677
+ ) -> sys:: GDExtensionBool {
678
+ let w: & mut C = CallableUserdata :: inner_from_raw ( callable_userdata) ;
679
+ let valid = w. is_valid ( ) ;
680
+
681
+ sys:: conv:: bool_to_sys ( valid)
682
+ }
683
+
684
+ // Implementing this is necessary because the default (nullptr) may consider custom callables as invalid in some cases.
685
+ pub unsafe extern "C" fn rust_callable_is_valid (
686
+ _callable_userdata : * mut std:: ffi:: c_void ,
687
+ ) -> sys:: GDExtensionBool {
688
+ // If we had an object (CallableCustomInfo::object_id field), we could check whether that object is alive.
689
+ // But since we just take a Rust function/closure, not knowing what happens inside, we assume always valid.
690
+ sys:: conv:: SYS_TRUE
691
+ }
644
692
}
0 commit comments