godot_core/registry/signal/connect_builder.rs
1/*
2 * Copyright (c) godot-rust; Bromeon and contributors.
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at https://mozilla.org/MPL/2.0/.
6 */
7
8use super::{make_callable_name, make_godot_fn};
9#[cfg(feature = "experimental-threads")] #[cfg_attr(published_docs, doc(cfg(feature = "experimental-threads")))]
10use crate::builtin::Callable;
11use crate::builtin::{GString, Variant};
12use crate::classes::object::ConnectFlags;
13use crate::meta;
14use crate::meta::{InParamTuple, ObjectToOwned};
15use crate::obj::{bounds, Bounds, Gd, GodotClass, WithSignals};
16use crate::registry::signal::signal_receiver::{IndirectSignalReceiver, SignalReceiver};
17use crate::registry::signal::{ConnectHandle, TypedSignal};
18
19/// Builder for customizing signal connections.
20///
21/// Allows a high degree of customization for connecting [`TypedSignal`], while maintaining complete type safety.
22///
23/// See the [Signals](https://godot-rust.github.io/book/register/signals.html) chapter in the book for a general introduction and examples.
24///
25/// # Customization
26/// Customizing your signal connection must be done **before** providing the function being connected
27/// (can be done by using of the `connect_*` methods) (see section `Finalizing` bellow).
28///
29/// All these methods are optional, and they can be combined.
30// Use HTML link due to conditional compilation; renders badly if target symbol is unavailable.
31/// - [`name()`][Self::name]: Name of the `Callable` (for debug purposes). \
32/// If not specified, the Rust function name is used. This is typically a good default, but not very readable for closures.
33/// - [`flags()`][Self::flags]: Provide one or multiple [`ConnectFlags`][crate::classes::object::ConnectFlags], possibly combined with bitwise OR.
34///
35/// # Finalizing
36/// After customizing your builder, you can register the connection with various `connect_*` functions.
37///
38/// To connect to **methods** (member functions) with a signal object, you have the following combinations:
39///
40/// | Signal object | 1st parameter `&mut C` | 1st parameter `&mut Gd<C>` |
41/// |---------------|------------------------------------------------|----------------------------------------------|
42/// | `self` | [`connect_self_mut`][Self::connect_self_mut] | [`connect_self_gd`][Self::connect_self_gd] |
43/// | other object | [`connect_other_mut`][Self::connect_other_mut] | [`connect_other_gd`][Self::connect_other_gd] |
44///
45/// Methods taking `&C` can (e.g. using interior mutability) can be indirectly connected through a `*_gd` overload + a `Gd::bind()` call.
46/// If this turns out to be a common use case, we could consider `connect_*_ref()` in the future.
47///
48/// <br>
49///
50/// For **global functions, associated functions and closures**, you can use the following APIs:
51/// - [`connect()`][Self::connect]: Connect any function running on the same thread as the signal emitter.
52/// - [`connect_sync()`](#method.connect_sync): Connect a global/associated function or closure that should be callable across threads.
53/// Allows signals to be emitted from other threads.
54/// - Requires `Send` + `Sync` bounds on the provided function.
55/// - Is only available for the Cargo feature `experimental-threads`.
56///
57/// # Implementation and documentation notes
58/// See [`TypedSignal` docs](struct.TypedSignal.html#implementation-and-documentation-notes) for a background on the `connect_*` API design.
59///
60/// <div class="warning">
61/// <strong>Warning:</strong>
62/// Exact type parameters are subject to change and not part of the public API. Since it's a type-state API, new states might require new
63/// type parameters. Thus, try not to name the <code>ConnectBuilder</code> type in your code; most connection setup doesn't need it.
64/// </div>
65// If naming the type becomes a requirement, there may be some options:
66// - Use a type alias in the module or TypedSignal, exposing only public parameters. This would work for constructor, but not all transformations.
67// - Pack multiple types together into "type lists", i.e. custom structs carrying the type state. For a user, this would appear as one type,
68// - which could also be #[doc(hidden)]. However, this may make the trait resolution more complex and worsen error messages, so not done now.
69#[must_use]
70pub struct ConnectBuilder<'ts, 'c, C: WithSignals, Ps> {
71 parent_sig: &'ts TypedSignal<'c, C, Ps>,
72 data: BuilderData,
73}
74
75/// Gathers all the non-typestate data, so that the builder can easily transfer it without manually moving each field.
76#[derive(Default)]
77struct BuilderData {
78 /// User-specified name; if not provided, the Rust RTTI type name of the function is used.
79 callable_name: Option<GString>,
80
81 /// Godot connection flags.
82 connect_flags: Option<ConnectFlags>,
83}
84
85#[allow(clippy::needless_lifetimes)] // 'ts + 'c are used conditionally.
86impl<'ts, 'c, C, Ps> ConnectBuilder<'ts, 'c, C, Ps>
87where
88 C: WithSignals,
89 Ps: meta::ParamTuple,
90{
91 pub(super) fn new(parent_sig: &'ts TypedSignal<'c, C, Ps>) -> Self {
92 ConnectBuilder {
93 parent_sig,
94 data: BuilderData::default(),
95 }
96 }
97
98 /// Name of the `Callable`, mostly used for debugging.
99 ///
100 /// If not provided, the Rust type name of the function/method is used.
101 pub fn name(mut self, name: impl meta::AsArg<GString>) -> Self {
102 assert!(
103 self.data.callable_name.is_none(),
104 "name() called twice on the same builder."
105 );
106
107 meta::arg_into_owned!(name);
108 self.data.callable_name = Some(name);
109 self
110 }
111
112 /// Add one or multiple flags to the connection, possibly combined with `|` operator.
113 pub fn flags(mut self, flags: ConnectFlags) -> Self {
114 assert!(
115 self.data.connect_flags.is_none(),
116 "flags() called twice on the same builder."
117 );
118
119 self.data.connect_flags = Some(flags);
120 self
121 }
122
123 /// Directly connect a Rust callable `godot_fn`, with a name based on `F`.
124 ///
125 /// This exists as a shorthand for the connect methods and avoids the generic instantiation of the full-blown
126 /// type state builder for simple + common connections, thus hopefully being a tiny bit lighter on compile times.
127 fn inner_connect_godot_fn<F>(
128 self,
129 godot_fn: impl FnMut(&[&Variant]) -> Variant + 'static,
130 bound: &Gd<impl GodotClass>,
131 ) -> ConnectHandle {
132 let callable_name = match &self.data.callable_name {
133 Some(user_provided_name) => user_provided_name,
134 None => &make_callable_name::<F>(),
135 };
136
137 let callable = bound.linked_callable(callable_name, godot_fn);
138 self.parent_sig
139 .inner_connect_untyped(callable, self.data.connect_flags)
140 }
141}
142
143impl<C: WithSignals, Ps: InParamTuple + 'static> ConnectBuilder<'_, '_, C, Ps> {
144 /// Connect a non-member function (global function, associated function or closure).
145 ///
146 /// Example usages:
147 /// ```ignore
148 /// sig.builder().connect(Self::static_func);
149 /// sig.builder().flags(ConnectFlags::DEFERRED).connect(global_func);
150 /// sig.connect(|arg| { /* closure */ });
151 /// ```
152 ///
153 /// # Related APIs
154 /// - To connect to a method on the object that owns this signal, use [`connect_self_mut()`][Self::connect_self_mut] or
155 /// [`connect_self_gd()`][Self::connect_self_gd].
156 /// - To connect to methods on other objects, use [`connect_other_mut()`][Self::connect_other_mut] or
157 /// [`connect_other_gd()`][Self::connect_other_gd].
158 /// - If you need [`connect flags`](ConnectFlags), call [`flags()`](Self::flags) before this.
159 /// - If you need cross-thread signals, use [`connect_sync()`](#method.connect_sync) instead (requires feature "experimental-threads").
160 pub fn connect<F>(self, mut function: F) -> ConnectHandle
161 where
162 for<'c_rcv> F: SignalReceiver<(), Ps>,
163 for<'c_rcv> IndirectSignalReceiver<'c_rcv, (), Ps, F>: From<&'c_rcv mut F>,
164 {
165 let godot_fn = make_godot_fn(move |args| {
166 IndirectSignalReceiver::from(&mut function)
167 .function()
168 .call((), args);
169 });
170
171 let bound = self.parent_sig.receiver_object();
172 self.inner_connect_godot_fn::<F>(godot_fn, &bound)
173 }
174
175 /// Connect a method with `&mut self` as the first parameter (user classes only).
176 ///
177 /// # Related APIs
178 /// - Use [`connect_self_gd()`][Self::connect_self_gd] to receive `Gd<Self>` instead and avoid implicit `bind_mut()` on emit. \
179 /// For engine classes, `&mut self` is not supported at all.
180 /// - To connect to methods on other objects, use [`connect_other_mut()`][Self::connect_other_mut].
181 /// - If you need [connect flags](ConnectFlags), call [`flags()`](Self::flags) before this.
182 /// - If you need cross-thread signals, use [`connect_sync()`](#method.connect_sync) instead (requires feature `experimental-threads`).
183 pub fn connect_self_mut<F>(self, mut function: F) -> ConnectHandle
184 where
185 C: Bounds<Declarer = bounds::DeclUser>,
186 for<'c_rcv> F: SignalReceiver<&'c_rcv mut C, Ps>,
187 for<'c_rcv> IndirectSignalReceiver<'c_rcv, &'c_rcv mut C, Ps, F>: From<&'c_rcv mut F>,
188 {
189 let mut gd = self.parent_sig.receiver_object();
190
191 let godot_fn = make_godot_fn(move |args| {
192 let mut guard = Gd::bind_mut(&mut gd);
193 IndirectSignalReceiver::from(&mut function)
194 .function()
195 .call(&mut *guard, args);
196 });
197
198 let bound = self.parent_sig.receiver_object();
199 self.inner_connect_godot_fn::<F>(godot_fn, &bound)
200 }
201
202 /// Connect a method with `&mut Gd<Self>` as the first parameter (user + engine classes).
203 ///
204 /// # Related APIs
205 /// - If your class `C` is user-defined and you'd like to have an automatic `bind_mut()` and receive `&mut self`, then
206 /// use [`connect_self_mut()`][Self::connect_self_mut] instead.
207 /// - To connect to methods on other objects, use [`connect_other_gd()`][Self::connect_other_gd].
208 /// - If you need [connect flags](ConnectFlags), call [`flags()`](Self::flags) before this.
209 /// - If you need cross-thread signals, use [`connect_sync()`](#method.connect_sync) instead (requires feature `experimental-threads`).
210 pub fn connect_self_gd<F>(self, mut function: F) -> ConnectHandle
211 where
212 F: SignalReceiver<Gd<C>, Ps>,
213 for<'c_rcv> IndirectSignalReceiver<'c_rcv, Gd<C>, Ps, F>: From<&'c_rcv mut F>,
214 {
215 let gd = self.parent_sig.receiver_object();
216 let bound = gd.clone();
217
218 let godot_fn = make_godot_fn(move |args| {
219 IndirectSignalReceiver::from(&mut function)
220 .function()
221 .call(gd.clone(), args);
222 });
223
224 self.inner_connect_godot_fn::<F>(godot_fn, &bound)
225 }
226
227 /// Connect a method with any `&mut OtherC` as the first parameter (user classes only).
228 ///
229 /// The parameter `object` can be of 2 different "categories":
230 /// - Any `&Gd<OtherC>` (e.g.: `&Gd<Node>`, `&Gd<MyClass>`).
231 /// - `&OtherC`, as long as `OtherC` is a user class that contains a `Base<T>` field (it implements the
232 /// [`WithBaseField`](crate::obj::WithBaseField) trait).
233 ///
234 /// # Related APIs
235 /// - Use [`connect_other_gd()`][Self::connect_other_gd] to receive `Gd<Self>` instead and avoid implicit `bind_mut()` on emit. \
236 /// For engine classes, `&mut self` is not supported at all.
237 /// - To connect to methods on the object that owns this signal, use [`connect_self_mut()`][Self::connect_self_mut].
238 /// - If you need [connect flags](ConnectFlags), call [`flags()`](Self::flags) before this.
239 /// - If you need cross-thread signals, use [`connect_sync()`](#method.connect_sync) instead (requires feature "experimental-threads").
240 pub fn connect_other_mut<F, OtherC>(
241 self,
242 object: &impl ObjectToOwned<OtherC>,
243 mut method: F,
244 ) -> ConnectHandle
245 where
246 OtherC: GodotClass + Bounds<Declarer = bounds::DeclUser>,
247 for<'c_rcv> F: SignalReceiver<&'c_rcv mut OtherC, Ps>,
248 for<'c_rcv> IndirectSignalReceiver<'c_rcv, &'c_rcv mut OtherC, Ps, F>: From<&'c_rcv mut F>,
249 {
250 let mut gd = object.object_to_owned();
251
252 let godot_fn = make_godot_fn(move |args| {
253 let mut guard = Gd::bind_mut(&mut gd);
254 IndirectSignalReceiver::from(&mut method)
255 .function()
256 .call(&mut *guard, args);
257 });
258
259 self.inner_connect_godot_fn::<F>(godot_fn, &object.object_to_owned())
260 }
261
262 /// Connect a method with any `&mut Gd<OtherC>` as the first parameter (user + engine classes).
263 ///
264 /// The parameter `object` can be of 2 different "categories":
265 /// - Any `&Gd<OtherC>` (e.g.: `&Gd<Node>`, `&Gd<MyClass>`).
266 /// - `&OtherC`, as long as `OtherC` is a user class that contains a `Base<T>` field (it implements the
267 /// [`WithBaseField`](crate::obj::WithBaseField) trait).
268 ///
269 /// # Related APIs
270 /// - To connect to methods on the object that owns this signal, use [`connect_self_gd()`][Self::connect_self_gd].
271 /// - If you need [connect flags](ConnectFlags), call [`flags()`](Self::flags) before this.
272 /// - If you need cross-thread signals, use [`connect_sync()`](#method.connect_sync) instead (requires feature "experimental-threads").
273 pub fn connect_other_gd<F, OtherC>(
274 self,
275 object: &impl ObjectToOwned<OtherC>,
276 mut method: F,
277 ) -> ConnectHandle
278 where
279 OtherC: GodotClass,
280 F: SignalReceiver<Gd<OtherC>, Ps>,
281 for<'c_rcv> IndirectSignalReceiver<'c_rcv, Gd<OtherC>, Ps, F>: From<&'c_rcv mut F>,
282 {
283 let gd = object.object_to_owned();
284
285 let godot_fn = make_godot_fn(move |args| {
286 IndirectSignalReceiver::from(&mut method)
287 .function()
288 .call(gd.clone(), args);
289 });
290
291 self.inner_connect_godot_fn::<F>(godot_fn, &object.object_to_owned())
292 }
293
294 /// Connect to this signal using a thread-safe function, allows the signal to be called across threads.
295 ///
296 /// Requires `Send` + `Sync` bounds on the provided function `F`, and is only available for the `experimental-threads`
297 /// Cargo feature.
298 ///
299 /// If you need [connect flags](ConnectFlags), call [`flags()`](Self::flags) before this.
300 #[cfg(feature = "experimental-threads")] #[cfg_attr(published_docs, doc(cfg(feature = "experimental-threads")))]
301 pub fn connect_sync<F>(self, mut function: F)
302 where
303 // Why both Send+Sync: closure can not only impact another thread (Sync), but it's also possible to share such Callables across threads
304 // (Send) or even call them from multiple threads (Sync). We don't differentiate the fine-grained needs, it's either thread-safe or not.
305 for<'c_rcv> F: SignalReceiver<(), Ps> + Send + Sync,
306 for<'c_rcv> IndirectSignalReceiver<'c_rcv, (), Ps, F>: From<&'c_rcv mut F>,
307 {
308 let godot_fn = make_godot_fn(move |args| {
309 IndirectSignalReceiver::from(&mut function)
310 .function()
311 .call((), args);
312 });
313
314 let callable_name = match &self.data.callable_name {
315 Some(user_provided_name) => user_provided_name,
316 None => &make_callable_name::<F>(),
317 };
318
319 let callable = Callable::from_sync_fn(callable_name, godot_fn);
320 self.parent_sig
321 .inner_connect_untyped(callable, self.data.connect_flags);
322 }
323}