Skip to content

Commit 3ef5d3e

Browse files
committed
ConnectBuilder: split _gd + _mut overloads
1 parent 0e40b4d commit 3ef5d3e

File tree

2 files changed

+108
-30
lines changed

2 files changed

+108
-30
lines changed

godot-core/src/registry/signal/connect_builder.rs

Lines changed: 70 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,9 @@ use crate::builtin::{Callable, GString, Variant};
1010
use crate::classes::object::ConnectFlags;
1111
use crate::meta;
1212
use crate::meta::FromGodot;
13-
use crate::obj::WithSignals;
14-
use crate::registry::signal::{GodotDeref, ToSignalObj, TypedSignal};
13+
use crate::obj::{bounds, Bounds, Gd, GodotClass, WithSignals};
14+
use crate::registry::signal::{ToSignalObj, TypedSignal};
1515
use std::fmt::Debug;
16-
use std::ops::DerefMut;
1716

1817
/// Builder for customizing signal connections.
1918
///
@@ -150,50 +149,94 @@ macro_rules! impl_builder_connect {
150149
self.inner_connect_godot_fn::<F>(godot_fn);
151150
}
152151

153-
/// Connect a method (member function) with `&mut self` as the first parameter.
152+
/// Connect a method with `&mut self` as the first parameter (user classes only).
154153
///
155-
/// - To connect to methods on other objects, use [`connect_other()`][Self::connect_other].
156-
/// - If you need [`connect flags`](ConnectFlags), call [`flags()`](Self::flags) before this.
157-
/// - If you need cross-thread signals, use [`connect_sync()`](#method.connect_sync) instead (requires feature "experimental-threads").
158-
pub fn connect_self<F, R, Decl>(self, mut function: F)
154+
/// - Use [`connect_self_gd()`][Self::connect_self_gd] to receive `Gd<Self>` instead and avoid implicit `bind_mut()` on emit.
155+
/// For engine classes, `&mut self` is not supported at all.
156+
/// - To connect to methods on other objects, use [`connect_other_mut()`][Self::connect_other_mut].
157+
/// - If you need [connect flags](ConnectFlags), call [`flags()`](Self::flags) before this.
158+
/// - If you need cross-thread signals, use [`connect_sync()`](#method.connect_sync) instead (requires feature `experimental-threads`).
159+
pub fn connect_self_mut<F, R>(self, mut function: F)
159160
where
161+
C: Bounds<Declarer = bounds::DeclUser>,
160162
F: FnMut(&mut C, $($Ps),*) -> R + 'static,
161-
C: GodotDeref<Decl>,
162163
{
163164
let mut gd = self.parent_sig.receiver_object();
164-
let godot_fn = make_godot_fn(move |($($args,)*):($($Ps,)*)| {
165-
let mut target = C::get_mut(&mut gd);
166-
let target_mut = target.deref_mut();
167-
function(target_mut, $($args),*);
165+
let godot_fn = make_godot_fn(move |($($args,)*): ($($Ps,)*)| {
166+
let mut guard = Gd::bind_mut(&mut gd);
167+
function(&mut *guard, $($args),*);
168+
});
169+
170+
self.inner_connect_godot_fn::<F>(godot_fn);
171+
}
172+
173+
/// Connect a method with `&mut Gd<Self>` as the first parameter (user + engine classes).
174+
///
175+
/// - If your class `C` is user-defined and you'd like to have an automatic `bind_mut()` and receive `&mut self`, then
176+
/// use [`connect_self_mut()`][Self::connect_self_mut] instead.
177+
/// - To connect to methods on other objects, use [`connect_other_gd()`][Self::connect_other_gd].
178+
/// - If you need [connect flags](ConnectFlags), call [`flags()`](Self::flags) before this.
179+
/// - If you need cross-thread signals, use [`connect_sync()`](#method.connect_sync) instead (requires feature `experimental-threads`).
180+
pub fn connect_self_gd<F, R>(self, mut function: F)
181+
where
182+
F: FnMut(&mut Gd<C>, $($Ps),*) -> R + 'static,
183+
{
184+
let mut gd = self.parent_sig.receiver_object();
185+
let godot_fn = make_godot_fn(move |($($args,)*): ($($Ps,)*)| {
186+
function(&mut gd, $($args),*);
168187
});
169188

170189
self.inner_connect_godot_fn::<F>(godot_fn);
171190
}
172191

173-
/// Connect a method (member function) with any `&mut OtherC` as the first parameter, where
174-
/// `OtherC`: [`GodotClass`](crate::obj::GodotClass) (both user and engine classes are accepted).
192+
/// Connect a method with any `&mut OtherC` as the first parameter (user classes only).
175193
///
176194
/// The parameter `object` can be of 2 different "categories":
177-
/// - Any `&Gd<OtherC>` (e.g.: `&Gd<Node>`, `&Gd<CustomUserClass>`).
178-
/// - `&OtherC`, as long as `OtherC` is a user class that contains a `base` field (it implements the
195+
/// - Any `&Gd<OtherC>` (e.g.: `&Gd<Node>`, `&Gd<MyClass>`).
196+
/// - `&OtherC`, as long as `OtherC` is a user class that contains a `Base<T>` field (it implements the
179197
/// [`WithBaseField`](crate::obj::WithBaseField) trait).
180198
///
181199
/// ---
182200
///
183-
/// - To connect to methods on the object that owns this signal, use [`connect_self()`][Self::connect_self].
184-
/// - If you need [`connect flags`](ConnectFlags), call [`flags()`](Self::flags) before this.
201+
/// - To connect to methods on the object that owns this signal, use [`connect_self_mut()`][Self::connect_self].
202+
/// - If you need [connect flags](ConnectFlags), call [`flags()`](Self::flags) before this.
185203
/// - If you need cross-thread signals, use [`connect_sync()`](#method.connect_sync) instead (requires feature "experimental-threads").
186-
pub fn connect_other<F, R, OtherC, Decl>(self, object: &impl ToSignalObj<OtherC>, mut method: F)
204+
pub fn connect_other_mut<F, R, OtherC>(self, object: &impl ToSignalObj<OtherC>, mut method: F)
187205
where
206+
OtherC: GodotClass + Bounds<Declarer = bounds::DeclUser>,
188207
F: FnMut(&mut OtherC, $($Ps),*) -> R + 'static,
189-
OtherC: GodotDeref<Decl>,
190208
{
191209
let mut gd = object.to_signal_obj();
192210

193-
let godot_fn = make_godot_fn(move |($($args,)*):($($Ps,)*)| {
194-
let mut target = OtherC::get_mut(&mut gd);
195-
let target_mut = target.deref_mut();
196-
method(target_mut, $($args),*);
211+
let godot_fn = make_godot_fn(move |($($args,)*): ($($Ps,)*)| {
212+
let mut guard = Gd::bind_mut(&mut gd);
213+
method(&mut *guard, $($args),*);
214+
});
215+
216+
self.inner_connect_godot_fn::<F>(godot_fn);
217+
}
218+
219+
/// Connect a method with any `&mut Gd<OtherC>` as the first parameter (user + engine classes).
220+
///
221+
/// The parameter `object` can be of 2 different "categories":
222+
/// - Any `&Gd<OtherC>` (e.g.: `&Gd<Node>`, `&Gd<MyClass>`).
223+
/// - `&OtherC`, as long as `OtherC` is a user class that contains a `Base<T>` field (it implements the
224+
/// [`WithBaseField`](crate::obj::WithBaseField) trait).
225+
///
226+
/// ---
227+
///
228+
/// - To connect to methods on the object that owns this signal, use [`connect_self_gd()`][Self::connect_self_gd].
229+
/// - If you need [connect flags](ConnectFlags), call [`flags()`](Self::flags) before this.
230+
/// - If you need cross-thread signals, use [`connect_sync()`](#method.connect_sync) instead (requires feature "experimental-threads").
231+
pub fn connect_other_gd<F, R, OtherC>(self, object: &impl ToSignalObj<OtherC>, mut method: F)
232+
where
233+
OtherC: GodotClass,
234+
F: FnMut(&mut Gd<OtherC>, $($Ps),*) -> R + 'static,
235+
{
236+
let mut gd = object.to_signal_obj();
237+
238+
let godot_fn = make_godot_fn(move |($($args,)*): ($($Ps,)*)| {
239+
method(&mut gd, $($args),*);
197240
});
198241

199242
self.inner_connect_godot_fn::<F>(godot_fn);
@@ -204,15 +247,15 @@ macro_rules! impl_builder_connect {
204247
/// Requires `Send` + `Sync` bounds on the provided function `F`, and is only available for the `experimental-threads`
205248
/// Cargo feature.
206249
///
207-
/// If you need [`connect flags`](ConnectFlags), call [`flags()`](Self::flags) before this.
250+
/// If you need [connect flags](ConnectFlags), call [`flags()`](Self::flags) before this.
208251
#[cfg(feature = "experimental-threads")]
209252
pub fn connect_sync<F, R>(self, mut function: F)
210253
where
211254
// Why both Send+Sync: closure can not only impact another thread (Sync), but it's also possible to share such Callables across threads
212255
// (Send) or even call them from multiple threads (Sync). We don't differentiate the fine-grained needs, it's either thread-safe or not.
213256
F: FnMut($($Ps),*) -> R + Send + Sync + 'static,
214257
{
215-
let godot_fn = make_godot_fn(move |($($args,)*):($($Ps,)*)| {
258+
let godot_fn = make_godot_fn(move |($($args,)*): ($($Ps,)*)| {
216259
function($($args),*);
217260
});
218261

itest/rust/src/builtin_tests/containers/signal_test.rs

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ fn signal_symbols_external_builder() {
159159
let receiver_mut = Receiver::new_alloc();
160160
sig.connect_builder()
161161
.name("receive_the_knowledge")
162-
.connect_other(&receiver_mut, Receiver::receive_int_mut);
162+
.connect_other_mut(&receiver_mut, Receiver::receive_int_mut);
163163

164164
sig.connect_other(&receiver_mut, Receiver::receive_int_mut);
165165

@@ -344,7 +344,7 @@ fn signal_symbols_connect_engine() {
344344
node.signals()
345345
.property_list_changed()
346346
.connect_builder()
347-
.connect_other(&engine, |this| {
347+
.connect_other_gd(&engine, |this| {
348348
assert_eq!(this.get_name(), StringName::from("hello"));
349349
});
350350

@@ -361,6 +361,7 @@ fn signal_symbols_connect_inferred() {
361361
let user = Emitter::new_alloc();
362362
let engine = Node::new_alloc();
363363

364+
// User signals.
364365
user.signals()
365366
.child_entered_tree()
366367
.connect_other(&engine, |this, mut child| {
@@ -379,18 +380,52 @@ fn signal_symbols_connect_inferred() {
379380
let _ = this.last_received_int;
380381
});
381382

383+
// User signals, builder.
384+
user.signals()
385+
.renamed()
386+
.connect_builder()
387+
.connect_self_mut(|this| {
388+
// Use method/field that `Emitter` declares.
389+
this.connect_base_signals_internal();
390+
let _ = this.last_received_int;
391+
});
392+
393+
// Engine signals.
382394
engine.signals().ready().connect_other(&user, |this| {
383395
// Use method/field that `Emitter` declares.
384396
this.connect_base_signals_internal();
385397
let _ = this.last_received_int;
386398
});
387399

400+
// Engine signals, builder.
388401
engine
389402
.signals()
390403
.tree_exiting()
391404
.connect_builder()
392405
.flags(ConnectFlags::DEFERRED)
393-
.connect_self(|this| {
406+
.connect_self_gd(|this| {
407+
// Use methods that `Node` declares.
408+
let _ = this.get_path(); // ref.
409+
this.set_unique_name_in_owner(true); // mut.
410+
});
411+
412+
engine
413+
.signals()
414+
.tree_exiting()
415+
.connect_builder()
416+
.connect_other_mut(&user, |this| {
417+
// Use methods that `Node` declares.
418+
use godot::obj::WithBaseField; // not recommended pattern; `*_gd()` connectors preferred.
419+
420+
let _ = this.base().get_path(); // ref.
421+
this.base_mut().set_unique_name_in_owner(true); // mut.
422+
});
423+
424+
engine
425+
.signals()
426+
.tree_exiting()
427+
.connect_builder()
428+
.connect_other_gd(&user, |this| {
394429
// Use methods that `Node` declares.
395430
let _ = this.get_path(); // ref.
396431
this.set_unique_name_in_owner(true); // mut.

0 commit comments

Comments
 (0)