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}