Skip to content

Commit ade4666

Browse files
committed
Split apply_deferred() -> run_deferred() + run_deferred_gd()
Also make methods inherent on `Gd`, not through dubious `UniformObjectDeref` + `ToSignalObj` trait impls.
1 parent 6588315 commit ade4666

File tree

4 files changed

+135
-50
lines changed

4 files changed

+135
-50
lines changed

godot-core/src/obj/call_deferred.rs

Lines changed: 64 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -5,64 +5,95 @@
55
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
66
*/
77

8-
use std::ops::DerefMut;
9-
10-
use godot_ffi::is_main_thread;
11-
12-
use crate::builtin::{Callable, Variant};
13-
use crate::meta::UniformObjectDeref;
14-
use crate::obj::bounds::Declarer;
15-
use crate::obj::GodotClass;
16-
use crate::registry::signal::ToSignalObj;
8+
use crate::obj::{Gd, GodotClass, WithBaseField};
179

1810
// TODO(v0.4): seal this and similar traits.
1911

20-
/// Enables `Gd::apply_deferred()` for type-safe deferred calls.
12+
/// Enables deferred execution for user classes containing a `Base<T>` field.
13+
///
14+
/// This trait provides `run_deferred()` and `run_deferred_gd()` methods for user class instances.
15+
/// For `Gd<T>` instances, use the inherent methods [`Gd::run_deferred()`] and [`Gd::run_deferred_gd()`] instead.
2116
///
22-
/// The trait is automatically available for all engine-defined Godot classes and user classes containing a `Base<T>` field.
17+
/// The trait is automatically available for user classes containing a `Base<T>` field.
2318
///
2419
/// # Usage
2520
///
2621
/// ```no_run
2722
/// # use godot::prelude::*;
28-
/// # use std::f32::consts::PI;
29-
/// fn some_fn(mut node: Gd<Node2D>) {
30-
/// node.apply_deferred(|n: &mut Node2D| n.rotate(PI))
23+
/// #
24+
/// #[derive(GodotClass)]
25+
/// #[class(init, base=Node2D)]
26+
/// struct MyNode {
27+
/// base: Base<Node2D>,
28+
/// }
29+
///
30+
/// #[godot_api]
31+
/// impl MyNode {
32+
/// fn some_method(&mut self) {
33+
/// self.run_deferred(|this: &mut MyNode| {
34+
/// // Direct access to Rust struct.
35+
/// });
36+
///
37+
/// self.run_deferred_gd(|gd: Gd<MyNode>| {
38+
/// // Access to Gd. Needs bind/bind_mut for struct access.
39+
/// });
40+
/// }
3141
/// }
3242
/// ```
33-
pub trait WithDeferredCall<T: GodotClass> {
43+
pub trait WithDeferredCall: GodotClass + WithBaseField {
3444
/// Defers the given closure to run during [idle time](https://docs.godotengine.org/en/stable/classes/class_object.html#class-object-method-call-deferred).
3545
///
3646
/// This is a type-safe alternative to [`Object::call_deferred()`][crate::classes::Object::call_deferred].
3747
///
48+
/// The closure receives `&mut Self` allowing direct access to Rust fields and methods.
49+
///
3850
/// # Panics
3951
/// If called outside the main thread.
52+
fn run_deferred<F>(&mut self, mut_self_method: F)
53+
where
54+
F: FnOnce(&mut Self) + 'static;
55+
56+
/// Defers the given closure to run during [idle time](https://docs.godotengine.org/en/stable/classes/class_object.html#class-object-method-call-deferred).
57+
///
58+
/// This is a type-safe alternative to [`Object::call_deferred()`][crate::classes::Object::call_deferred].
59+
///
60+
/// The closure receives `Gd<Self>` which can be used to call engine methods.
61+
///
62+
/// # Panics
63+
/// If called outside the main thread.
64+
fn run_deferred_gd<F>(&mut self, gd_function: F)
65+
where
66+
F: FnOnce(Gd<Self>) + 'static;
67+
68+
#[deprecated(
69+
since = "0.4.0",
70+
note = "Split into `run_deferred()` + `run_deferred_gd`"
71+
)]
4072
fn apply_deferred<F>(&mut self, rust_function: F)
4173
where
42-
F: FnOnce(&mut T) + 'static;
74+
F: FnOnce(&mut Self) + 'static,
75+
{
76+
self.run_deferred(rust_function)
77+
}
4378
}
4479

45-
impl<T, S, D> WithDeferredCall<T> for S
80+
impl<T> WithDeferredCall for T
4681
where
47-
T: UniformObjectDeref<D, Declarer = D>,
48-
S: ToSignalObj<T>,
49-
D: Declarer,
82+
T: WithBaseField,
5083
{
51-
fn apply_deferred<'a, F>(&mut self, rust_function: F)
84+
fn run_deferred<F>(&mut self, mut_self_method: F)
5285
where
53-
F: FnOnce(&mut T) + 'static,
86+
F: FnOnce(&mut Self) + 'static,
5487
{
55-
assert!(
56-
is_main_thread(),
57-
"`apply_deferred` must be called on the main thread"
58-
);
88+
// We need to copy the Gd, because the lifetime of `&mut self` does not extend throughout the closure, which will only be called
89+
// deferred. It might even be freed in-between, causing panic on bind_mut().
90+
self.to_gd().run_deferred(mut_self_method)
91+
}
5992

60-
let mut this = self.to_signal_obj().clone();
61-
let callable = Callable::from_once_fn("apply_deferred", move |_| {
62-
let mut this_mut = T::object_as_mut(&mut this);
63-
rust_function(this_mut.deref_mut());
64-
Ok(Variant::nil())
65-
});
66-
callable.call_deferred(&[]);
93+
fn run_deferred_gd<F>(&mut self, gd_function: F)
94+
where
95+
F: FnOnce(Gd<Self>) + 'static,
96+
{
97+
self.to_gd().run_deferred_gd(gd_function)
6798
}
6899
}

godot-core/src/obj/gd.rs

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use std::fmt::{Debug, Display, Formatter, Result as FmtResult};
99
use std::ops::{Deref, DerefMut};
1010

1111
use godot_ffi as sys;
12+
use godot_ffi::is_main_thread;
1213
use sys::{static_assert_eq_size_align, SysPtr as _};
1314

1415
use crate::builtin::{Callable, GString, NodePath, StringName, Variant};
@@ -19,7 +20,7 @@ use crate::meta::{
1920
};
2021
use crate::obj::{
2122
bounds, cap, Bounds, DynGd, GdDerefTarget, GdMut, GdRef, GodotClass, Inherits, InstanceId,
22-
OnEditor, RawGd, WithSignals,
23+
OnEditor, RawGd, WithBaseField, WithSignals,
2324
};
2425
use crate::private::{callbacks, PanicPayload};
2526
use crate::registry::class::try_dynify_object;
@@ -665,6 +666,54 @@ impl<T: GodotClass> Gd<T> {
665666
// Do not increment ref-count; assumed to be return value from FFI.
666667
sys::ptr_then(object_ptr, |ptr| Gd::from_obj_sys_weak(ptr))
667668
}
669+
670+
/// Defers the given closure to run during [idle time](https://docs.godotengine.org/en/stable/classes/class_object.html#class-object-method-call-deferred).
671+
///
672+
/// This is a type-safe alternative to [`Object::call_deferred()`][crate::classes::Object::call_deferred].
673+
///
674+
/// The closure receives `&mut T` allowing direct access to Rust fields and methods.
675+
/// This method is only available for user-defined classes with a `Base<T>` field.
676+
///
677+
/// For engine classes, use [`run_deferred_gd()`][Self::run_deferred_gd] instead.
678+
///
679+
/// # Panics
680+
/// If called outside the main thread.
681+
pub fn run_deferred<F>(&mut self, mut_self_method: F)
682+
where
683+
T: WithBaseField,
684+
F: FnOnce(&mut T) + 'static,
685+
{
686+
self.run_deferred_gd(move |mut gd| {
687+
let mut guard = gd.bind_mut();
688+
mut_self_method(&mut *guard);
689+
});
690+
}
691+
692+
/// Defers the given closure to run during [idle time](https://docs.godotengine.org/en/stable/classes/class_object.html#class-object-method-call-deferred).
693+
///
694+
/// This is a type-safe alternative to [`Object::call_deferred()`][crate::classes::Object::call_deferred].
695+
///
696+
/// The closure receives a `Gd<T>` which can be used to call engine methods.
697+
/// This method works for any Godot class (engine or user-defined).
698+
///
699+
/// # Panics
700+
/// If called outside the main thread.
701+
pub fn run_deferred_gd<F>(&mut self, gd_function: F)
702+
where
703+
F: FnOnce(Gd<T>) + 'static,
704+
{
705+
let obj = self.clone();
706+
assert!(
707+
is_main_thread(),
708+
"`run_deferred` must be called on the main thread"
709+
);
710+
711+
let callable = Callable::from_once_fn("run_deferred", move |_| {
712+
gd_function(obj);
713+
Ok(Variant::nil())
714+
});
715+
callable.call_deferred(&[]);
716+
}
668717
}
669718

670719
/// _The methods in this impl block are only available for objects `T` that are manually managed,

godot-macros/src/class/data_models/signal.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -422,7 +422,7 @@ fn make_signal_symbols(
422422
// Earlier implementation generated a simplified code when no #[signal] was declared: only WithSignals/WithUserSignals impl, but no own
423423
// collection, instead the associated type pointing to the base class. This has however some problems:
424424
// * Part of the reason for user-defined collection is to store UserSignalObject instead of Gd, which can store &mut self.
425-
// This is necessary for self.signals().some_base_signal().emit(), if such a signal is connected to Self::method_mut;
425+
// This is necessary for self.signals().some_base_signal().emit(), if such a signal is connected to Self::connect*() taking &mut self;
426426
// Gd would cause a borrow error.
427427
// * Once we add Rust-Rust inheritance, we'd need to differentiate case again, which can be tricky since #[godot_api] has no information
428428
// about the base class.

itest/rust/src/object_tests/call_deferred_test.rs

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@ impl DeferredTestNode {
3131
self.base_mut().set_name(ACCEPTED_NAME);
3232
}
3333

34+
fn accept_gd(mut this: Gd<Self>) {
35+
this.set_name(ACCEPTED_NAME);
36+
}
37+
3438
fn create_assertion_task(&mut self) -> TaskHandle {
3539
assert_ne!(
3640
self.base().get_name().to_string(),
@@ -69,42 +73,43 @@ fn call_deferred_untyped(ctx: &crate::framework::TestContext) -> TaskHandle {
6973
// Called through Godot and therefore requires #[func] on the method.
7074
test_node.call_deferred("accept", &[]);
7175

72-
let mut gd_mut = test_node.bind_mut();
73-
gd_mut.create_assertion_task()
76+
let mut guard = test_node.bind_mut();
77+
guard.create_assertion_task()
7478
}
7579

7680
#[itest(async)]
77-
fn call_deferred_godot_class(ctx: &crate::framework::TestContext) -> TaskHandle {
81+
fn run_deferred_user_class(ctx: &crate::framework::TestContext) -> TaskHandle {
7882
let mut test_node = DeferredTestNode::new_alloc();
7983
ctx.scene_tree.clone().add_child(&test_node);
8084

81-
let mut gd_mut = test_node.bind_mut();
85+
let mut guard = test_node.bind_mut();
86+
8287
// Explicitly check that this can be invoked on &mut T.
83-
let godot_class_ref: &mut DeferredTestNode = gd_mut.deref_mut();
84-
godot_class_ref.apply_deferred(DeferredTestNode::accept);
88+
let godot_class_ref: &mut DeferredTestNode = guard.deref_mut();
89+
godot_class_ref.run_deferred(DeferredTestNode::accept);
8590

86-
gd_mut.create_assertion_task()
91+
guard.create_assertion_task()
8792
}
8893

8994
#[itest(async)]
90-
fn call_deferred_gd_user_class(ctx: &crate::framework::TestContext) -> TaskHandle {
95+
fn run_deferred_gd_user_class(ctx: &crate::framework::TestContext) -> TaskHandle {
9196
let mut test_node = DeferredTestNode::new_alloc();
9297
ctx.scene_tree.clone().add_child(&test_node);
9398

94-
test_node.apply_deferred(DeferredTestNode::accept);
99+
test_node.run_deferred_gd(DeferredTestNode::accept_gd);
95100

96-
let mut gd_mut = test_node.bind_mut();
97-
gd_mut.create_assertion_task()
101+
let mut guard = test_node.bind_mut();
102+
guard.create_assertion_task()
98103
}
99104

100105
#[itest(async)]
101-
fn call_deferred_gd_engine_class(ctx: &crate::framework::TestContext) -> TaskHandle {
106+
fn run_deferred_engine_class(ctx: &crate::framework::TestContext) -> TaskHandle {
102107
let mut test_node = DeferredTestNode::new_alloc();
103108
ctx.scene_tree.clone().add_child(&test_node);
104109

105110
let mut node = test_node.clone().upcast::<Node>();
106-
node.apply_deferred(|that_node| that_node.set_name(ACCEPTED_NAME));
111+
node.run_deferred_gd(|mut that_node| that_node.set_name(ACCEPTED_NAME));
107112

108-
let mut gd_mut = test_node.bind_mut();
109-
gd_mut.create_assertion_task()
113+
let mut guard = test_node.bind_mut();
114+
guard.create_assertion_task()
110115
}

0 commit comments

Comments
 (0)