Skip to content

Commit 2b09b62

Browse files
authored
Merge pull request #1327 from godot-rust/qol/run-deferred
Split `apply_deferred()` -> `run_deferred()` + `run_deferred_gd()`
2 parents 6588315 + b7f193e commit 2b09b62

File tree

7 files changed

+107
-89
lines changed

7 files changed

+107
-89
lines changed

godot-core/src/obj/call_deferred.rs

Lines changed: 0 additions & 68 deletions
This file was deleted.

godot-core/src/obj/gd.rs

Lines changed: 51 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,55 @@ 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]. The closure receives
673+
/// `&mut Self` allowing direct access to Rust fields and methods.
674+
///
675+
/// This method is only available for user-defined classes with a `Base<T>` field.
676+
/// For engine classes, use [`run_deferred_gd()`][Self::run_deferred_gd] instead.
677+
///
678+
/// See also [`WithBaseField::run_deferred()`] if you are within an `impl` block and have access to `self`.
679+
///
680+
/// # Panics
681+
/// If called outside the main thread.
682+
pub fn run_deferred<F>(&mut self, mut_self_method: F)
683+
where
684+
T: WithBaseField,
685+
F: FnOnce(&mut T) + 'static,
686+
{
687+
self.run_deferred_gd(move |mut gd| {
688+
let mut guard = gd.bind_mut();
689+
mut_self_method(&mut *guard);
690+
});
691+
}
692+
693+
/// Defers the given closure to run during [idle time](https://docs.godotengine.org/en/stable/classes/class_object.html#class-object-method-call-deferred).
694+
///
695+
/// This is a type-safe alternative to [`Object::call_deferred()`][crate::classes::Object::call_deferred]. The closure receives
696+
/// `Gd<T>`, which can be used to call engine methods or [`bind()`][Gd::bind]/[`bind_mut()`][Gd::bind_mut] to access the Rust object.
697+
///
698+
/// See also [`WithBaseField::run_deferred_gd()`] if you are within an `impl` block and have access to `self`.
699+
///
700+
/// # Panics
701+
/// If called outside the main thread.
702+
pub fn run_deferred_gd<F>(&mut self, gd_function: F)
703+
where
704+
F: FnOnce(Gd<T>) + 'static,
705+
{
706+
let obj = self.clone();
707+
assert!(
708+
is_main_thread(),
709+
"`run_deferred` must be called on the main thread"
710+
);
711+
712+
let callable = Callable::from_once_fn("run_deferred", move |_| {
713+
gd_function(obj);
714+
Ok(Variant::nil())
715+
});
716+
callable.call_deferred(&[]);
717+
}
668718
}
669719

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

godot-core/src/obj/mod.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
//! * [`Gd`], a smart pointer that manages instances of Godot classes.
1313
1414
mod base;
15-
mod call_deferred;
1615
mod casts;
1716
mod dyn_gd;
1817
mod gd;
@@ -27,7 +26,6 @@ mod traits;
2726
pub(crate) mod rtti;
2827

2928
pub use base::*;
30-
pub use call_deferred::WithDeferredCall;
3129
pub use dyn_gd::DynGd;
3230
pub use gd::*;
3331
pub use guards::{BaseMut, BaseRef, DynGdMut, DynGdRef, GdMut, GdRef};

godot-core/src/obj/traits.rs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -540,6 +540,40 @@ pub trait WithBaseField: GodotClass + Bounds<Declarer = bounds::DeclUser> {
540540
// Narrows lifetime again from 'static to 'self.
541541
BaseMut::new(passive_gd, guard)
542542
}
543+
544+
/// Defers the given closure to run during [idle time](https://docs.godotengine.org/en/stable/classes/class_object.html#class-object-method-call-deferred).
545+
///
546+
/// This is a type-safe alternative to [`Object::call_deferred()`][crate::classes::Object::call_deferred]. The closure receives
547+
/// `&mut Self` allowing direct access to Rust fields and methods.
548+
///
549+
/// See also [`Gd::run_deferred()`] to defer logic outside of `self`.
550+
///
551+
/// # Panics
552+
/// If called outside the main thread.
553+
fn run_deferred<F>(&mut self, mut_self_method: F)
554+
where
555+
F: FnOnce(&mut Self) + 'static,
556+
{
557+
// We need to copy the Gd, because the lifetime of `&mut self` does not extend throughout the closure, which will only be called
558+
// deferred. It might even be freed in-between, causing panic on bind_mut().
559+
self.to_gd().run_deferred(mut_self_method)
560+
}
561+
562+
/// Defers the given closure to run during [idle time](https://docs.godotengine.org/en/stable/classes/class_object.html#class-object-method-call-deferred).
563+
///
564+
/// This is a type-safe alternative to [`Object::call_deferred()`][crate::classes::Object::call_deferred]. The closure receives
565+
/// `Gd<Self>`, which can be used to call engine methods or [`bind()`][Gd::bind]/[`bind_mut()`][Gd::bind_mut] to access the Rust object.
566+
///
567+
/// See also [`Gd::run_deferred_gd()`] to defer logic outside of `self`.
568+
///
569+
/// # Panics
570+
/// If called outside the main thread.
571+
fn run_deferred_gd<F>(&mut self, gd_function: F)
572+
where
573+
F: FnOnce(Gd<Self>) + 'static,
574+
{
575+
self.to_gd().run_deferred_gd(gd_function)
576+
}
543577
}
544578

545579
/// Implemented for all classes with registered signals, both engine- and user-declared.

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.

godot/src/prelude.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,7 @@ mod trait_reexports {
3333
pub use crate::obj::EngineEnum as _;
3434
pub use crate::obj::NewAlloc as _;
3535
pub use crate::obj::NewGd as _;
36-
pub use crate::obj::WithBaseField as _; // base(), base_mut(), to_gd()
37-
pub use crate::obj::WithDeferredCall as _; // apply_deferred()
36+
pub use crate::obj::WithBaseField as _; // base(), base_mut(), to_gd(), run_deferred(), run_deferred_gd()
3837
pub use crate::obj::WithSignals as _; // Gd::signals()
3938
pub use crate::obj::WithUserSignals as _; // self.signals()
4039
}

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)