Skip to content

Commit 0cc9408

Browse files
committed
feat(core): support easier, type-safe deferred calls
1 parent eb9253a commit 0cc9408

File tree

2 files changed

+32
-1
lines changed

2 files changed

+32
-1
lines changed

godot-core/src/obj/gd.rs

Lines changed: 20 additions & 0 deletions
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, NodePath, StringName, Variant};
@@ -444,6 +445,25 @@ impl<T: GodotClass> Gd<T> {
444445
})
445446
}
446447

448+
/// Runs the given Closure deferred.
449+
///
450+
/// This can be a type-safe alternative to [`classes::Object::call_deferred`], but does not handle dynamic dispatch, unless explicitly used.
451+
/// This must be used on the main thread.
452+
#[cfg(since_api = "4.2")]
453+
pub fn apply_deferred<F>(&mut self, mut rust_function: F)
454+
where
455+
F: FnMut(&mut T) + 'static,
456+
T: GodotClass + Bounds<Declarer = bounds::DeclUser>,
457+
{
458+
assert!(!is_main_thread(), "apply_deferred must be called on main thread. Consider bind_deferred instead");
459+
let this = self.clone();
460+
let callable = Callable::from_local_fn("apply_deferred", move |_| {
461+
rust_function(this.clone().bind_mut().deref_mut());
462+
Ok(Variant::nil())
463+
});
464+
callable.call_deferred(&[]);
465+
}
466+
447467
/// Returns `Ok(cast_obj)` on success, `Err(self)` on error.
448468
// Visibility: used by DynGd.
449469
pub(crate) fn owned_cast<U>(self) -> Result<Gd<U>, Self>

itest/rust/src/object_tests/deferred_call_test.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,9 +67,20 @@ impl INode for DeferredTestNode {
6767
fn calls_method_names_deferred(ctx: &crate::framework::TestContext) -> TaskHandle {
6868
let mut test_node = DeferredTestNode::new_alloc();
6969
ctx.scene_tree.clone().add_child(&test_node);
70-
70+
7171
test_node.call_deferred("accept", &[]);
7272

7373
let handle = test_node.bind().as_expectation_task();
7474
handle
7575
}
76+
77+
#[itest(async)]
78+
fn calls_closure_deferred(ctx: &crate::framework::TestContext) -> TaskHandle {
79+
let mut test_node = DeferredTestNode::new_alloc();
80+
ctx.scene_tree.clone().add_child(&test_node);
81+
82+
test_node.apply_deferred(DeferredTestNode::accept);
83+
84+
let handle = test_node.bind().as_expectation_task();
85+
handle
86+
}

0 commit comments

Comments
 (0)