From 546dbd646e59a3f4bf5c459ec7095f601044973e Mon Sep 17 00:00:00 2001 From: Iride Massidda Date: Sat, 13 Dec 2025 22:25:14 +0100 Subject: [PATCH 1/3] docs(typed_signal): doctests for connect_self and connect_other + availability paragraph from book --- .../src/registry/signal/typed_signal.rs | 99 +++++++++++++++++-- 1 file changed, 93 insertions(+), 6 deletions(-) diff --git a/godot-core/src/registry/signal/typed_signal.rs b/godot-core/src/registry/signal/typed_signal.rs index 7e2f3a78a..e3ffdc602 100644 --- a/godot-core/src/registry/signal/typed_signal.rs +++ b/godot-core/src/registry/signal/typed_signal.rs @@ -5,10 +5,6 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -use std::borrow::Cow; -use std::marker::PhantomData; -use std::ops::DerefMut; - use super::{make_callable_name, make_godot_fn, ConnectBuilder, ConnectHandle, SignalObject}; use crate::builtin::{Callable, Variant}; use crate::classes::object::ConnectFlags; @@ -16,7 +12,9 @@ use crate::meta; use crate::meta::{InParamTuple, ObjectToOwned, UniformObjectDeref}; use crate::obj::{Gd, GodotClass, WithSignals}; use crate::registry::signal::signal_receiver::{IndirectSignalReceiver, SignalReceiver}; - +use std::borrow::Cow; +use std::marker::PhantomData; +use std::ops::DerefMut; // ---------------------------------------------------------------------------------------------------------------------------------------------- /// Type-safe version of a Godot signal. @@ -42,6 +40,7 @@ use crate::registry::signal::signal_receiver::{IndirectSignalReceiver, SignalRec /// - [`builder()`][Self::builder] for more complex setups (such as choosing [`ConnectFlags`] or making thread-safe connections). /// /// # Emitting a signal +// -------------------- /// Code-generated signal types provide a method `emit(...)`, which adopts the names and types of the `#[signal]` parameter list. /// In most cases, that's the method you are looking for. /// @@ -49,6 +48,16 @@ use crate::registry::signal::signal_receiver::{IndirectSignalReceiver, SignalRec /// /// # Generic programming and code reuse /// If you want to build higher-level abstractions that operate on `TypedSignal`, you will need the [`SignalReceiver`] trait. +/// +// Keep in sync with https://godot-rust.github.io/book/register/signals.html#admonition-availability-of-signal-api. +/// # Availability +/// For typed signals to be available, you need: +/// - A `#[godot_api] impl MyClass {}` block. +/// - This must be an inherent impl, the `I*` trait `impl` won't be enough. +/// - Leave the impl empty if necessary. +/// - A `Base` field. +/// +/// Signals, typed or not, cannot be declared in secondary impl blocks (those annotated with `#[godot_api(secondary)]` attribute). pub struct TypedSignal<'c, C: WithSignals, Ps> { /// In Godot, valid signals (unlike funcs) are _always_ declared in a class and become part of each instance. So there's always an object. object: C::__SignalObj<'c>, @@ -189,6 +198,48 @@ impl TypedSignal<'_, C, Ps> { /// /// - To connect to methods on other objects, use [`connect_other()`][Self::connect_other]. /// - If you need [`connect flags`](ConnectFlags) or cross-thread signals, use [`builder()`][Self::builder]. + /// + /// # Example + /// ```no_run + /// # use godot::prelude::*; + /// # #[derive(GodotClass)] + /// # #[class(init, base=Node)] + /// # pub struct Player { + /// # health_ui: OnEditor>, + /// # base: Base, + /// # } + /// # #[derive(GodotClass)] + /// # #[class(init, base=Node2D)] + /// # pub struct HealthUI { + /// # base: Base, + /// # } + /// # #[godot_api] + /// # impl Player { + /// # #[signal] + /// # fn health_changed(health: i32); + /// # } + /// impl Player { + /// fn change_health_anim(&mut self, health: i32) { + /// // Change animation based on health + /// } + /// } + /// impl HealthUI { + /// fn on_health_changed(&mut self, health: i32) { + /// // Change healthbar UI based on health + /// } + /// } + /// #[godot_api] + /// impl INode for Player { + /// fn ready(&mut self) { + /// self.signals() // Connect to self + /// .health_changed() + /// .connect_self(Self::change_health_anim); + /// self.signals() // Connect to other object (health_ui is OnEditor>) + /// .health_changed() + /// .connect_self(|s, amount| s.health_ui.bind_mut().on_health_changed(amount)); + /// } + /// } + /// ``` pub fn connect_self(&self, mut function: F) -> ConnectHandle where for<'c_rcv> F: SignalReceiver<&'c_rcv mut C, Ps> + 'static, @@ -214,11 +265,47 @@ impl TypedSignal<'_, C, Ps> { /// - Any `&Gd` (e.g.: `&Gd`, `&Gd`). /// - `&OtherC`, as long as `OtherC` is a user class that contains a `base` field (it implements the /// [`WithBaseField`][crate::obj::WithBaseField] trait). - /// /// --- /// /// - To connect to methods on the object that owns this signal, use [`connect_self()`][Self::connect_self]. /// - If you need [`connect flags`](ConnectFlags) or cross-thread signals, use [`builder()`][Self::builder]. + /// + /// # Example + /// ```no_run + /// # use godot::prelude::*; + /// # use godot::classes::{Button, Control, IControl}; + /// # #[derive(GodotClass)] + /// # #[class(init, base=Control)] + /// # pub struct OtherTool { + /// # base: Base, + /// # } + /// # #[derive(GodotClass)] + /// # #[class(init, base=Control)] + /// # pub struct Tool { + /// # button: OnEditor>, + /// # other_tool: OnEditor>, + /// # base: Base, + /// # } + /// impl OtherTool { + /// fn set_tool_enabled(&mut self, value: bool) { } + /// } + /// impl Tool { + /// fn execute_tool(&mut self) { } + /// } + /// #[godot_api] + /// impl IControl for Tool { + /// fn ready(&mut self) { + /// self.button // Connect from other to self + /// .signals() + /// .pressed() + /// .connect_other(self, Self::execute_tool); + /// self.button // Connect from other to other (other_tool is OnEditor>) + /// .signals() + /// .pressed() + /// .connect_other(&*self.other_tool, |tool| tool.set_tool_enabled(false)); + /// } + /// } + /// ``` pub fn connect_other( &self, object: &impl ObjectToOwned, From cedb2068fee5cbcdfc8c175099b1146e35afbfc5 Mon Sep 17 00:00:00 2001 From: Jan Haller Date: Sun, 14 Dec 2025 11:28:53 +0100 Subject: [PATCH 2/3] Formatting --- .../src/registry/signal/typed_signal.rs | 53 ++++++++++--------- 1 file changed, 27 insertions(+), 26 deletions(-) diff --git a/godot-core/src/registry/signal/typed_signal.rs b/godot-core/src/registry/signal/typed_signal.rs index e3ffdc602..41b5243fe 100644 --- a/godot-core/src/registry/signal/typed_signal.rs +++ b/godot-core/src/registry/signal/typed_signal.rs @@ -5,6 +5,10 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ +use std::borrow::Cow; +use std::marker::PhantomData; +use std::ops::DerefMut; + use super::{make_callable_name, make_godot_fn, ConnectBuilder, ConnectHandle, SignalObject}; use crate::builtin::{Callable, Variant}; use crate::classes::object::ConnectFlags; @@ -12,10 +16,6 @@ use crate::meta; use crate::meta::{InParamTuple, ObjectToOwned, UniformObjectDeref}; use crate::obj::{Gd, GodotClass, WithSignals}; use crate::registry::signal::signal_receiver::{IndirectSignalReceiver, SignalReceiver}; -use std::borrow::Cow; -use std::marker::PhantomData; -use std::ops::DerefMut; -// ---------------------------------------------------------------------------------------------------------------------------------------------- /// Type-safe version of a Godot signal. /// @@ -205,38 +205,36 @@ impl TypedSignal<'_, C, Ps> { /// # #[derive(GodotClass)] /// # #[class(init, base=Node)] /// # pub struct Player { - /// # health_ui: OnEditor>, + /// # ui: OnEditor>, /// # base: Base, /// # } /// # #[derive(GodotClass)] /// # #[class(init, base=Node2D)] - /// # pub struct HealthUI { + /// # pub struct HealthUi { /// # base: Base, /// # } - /// # #[godot_api] - /// # impl Player { - /// # #[signal] - /// # fn health_changed(health: i32); - /// # } - /// impl Player { - /// fn change_health_anim(&mut self, health: i32) { - /// // Change animation based on health - /// } - /// } - /// impl HealthUI { - /// fn on_health_changed(&mut self, health: i32) { - /// // Change healthbar UI based on health - /// } - /// } + /// #[godot_api] + /// impl Player { + /// #[signal] + /// fn health_changed(health: i32); + /// + /// fn change_health_anim(&mut self, health: i32) { /* ... */ } + /// } + /// impl HealthUi { + /// fn on_health_changed(&mut self, health: i32) { /* ... */ } + /// } /// #[godot_api] /// impl INode for Player { /// fn ready(&mut self) { - /// self.signals() // Connect to self + /// // Connect to this object. + /// self.signals() /// .health_changed() /// .connect_self(Self::change_health_anim); - /// self.signals() // Connect to other object (health_ui is OnEditor>) + /// + /// // Connect to other object, where ui: OnEditor>. + /// self.signals() /// .health_changed() - /// .connect_self(|s, amount| s.health_ui.bind_mut().on_health_changed(amount)); + /// .connect_self(|this, amount| this.ui.bind_mut().on_health_changed(amount)); /// } /// } /// ``` @@ -295,11 +293,14 @@ impl TypedSignal<'_, C, Ps> { /// #[godot_api] /// impl IControl for Tool { /// fn ready(&mut self) { - /// self.button // Connect from other to self + /// // Connect from other to self. + /// self.button /// .signals() /// .pressed() /// .connect_other(self, Self::execute_tool); - /// self.button // Connect from other to other (other_tool is OnEditor>) + /// + /// // Connect from other to other, where other_tool: OnEditor>. + /// self.button /// .signals() /// .pressed() /// .connect_other(&*self.other_tool, |tool| tool.set_tool_enabled(false)); From 79444274deee5918850f93dbe3c24fa3b4fd8968 Mon Sep 17 00:00:00 2001 From: Iride Massidda Date: Sun, 14 Dec 2025 13:32:18 +0100 Subject: [PATCH 3/3] feat: new docexample on top of the page, removing others --- .../src/registry/signal/typed_signal.rs | 121 ++++++------------ 1 file changed, 41 insertions(+), 80 deletions(-) diff --git a/godot-core/src/registry/signal/typed_signal.rs b/godot-core/src/registry/signal/typed_signal.rs index 41b5243fe..aff386440 100644 --- a/godot-core/src/registry/signal/typed_signal.rs +++ b/godot-core/src/registry/signal/typed_signal.rs @@ -58,6 +58,47 @@ use crate::registry::signal::signal_receiver::{IndirectSignalReceiver, SignalRec /// - A `Base` field. /// /// Signals, typed or not, cannot be declared in secondary impl blocks (those annotated with `#[godot_api(secondary)]` attribute). +/// # Examples +/// ```no_run +/// # use godot::prelude::*; +/// # use godot::classes::Button; +/// # #[derive(GodotClass)] +/// # #[class(init,base=Node)] +/// # pub struct ExampleClass { +/// # button: OnEditor>, +/// # other_button: OnEditor>, +/// # base: Base, +/// # } +/// #[godot_api] +/// impl ExampleClass { +/// #[signal] +/// fn signal(); +/// fn method(&mut self) { /* ... */ } +/// fn method_with_args(&mut self, value: bool) { /* ... */ } +/// } +/// impl ExampleClass { +/// fn example(&mut self) { +/// // Connect Self to Self (Self::method). +/// self.signals().signal().connect_self(Self::method); +/// // Connect Self to Self (closure). +/// self.signals() +/// .signal() +/// .connect_self(|s| s.method_with_args(true)); +/// // Connect other to Self (Self::method). +/// self.button +/// .signals() +/// .pressed() +/// .connect_other(self, Self::method); +/// // Connect other to other (closure). +/// self.button +/// .signals() +/// .toggled() +/// .connect_other(&*self.other_button, |other_button, toggled| { +/// other_button.set_pressed(toggled) +/// }); +/// } +/// } +/// ``` pub struct TypedSignal<'c, C: WithSignals, Ps> { /// In Godot, valid signals (unlike funcs) are _always_ declared in a class and become part of each instance. So there's always an object. object: C::__SignalObj<'c>, @@ -198,46 +239,6 @@ impl TypedSignal<'_, C, Ps> { /// /// - To connect to methods on other objects, use [`connect_other()`][Self::connect_other]. /// - If you need [`connect flags`](ConnectFlags) or cross-thread signals, use [`builder()`][Self::builder]. - /// - /// # Example - /// ```no_run - /// # use godot::prelude::*; - /// # #[derive(GodotClass)] - /// # #[class(init, base=Node)] - /// # pub struct Player { - /// # ui: OnEditor>, - /// # base: Base, - /// # } - /// # #[derive(GodotClass)] - /// # #[class(init, base=Node2D)] - /// # pub struct HealthUi { - /// # base: Base, - /// # } - /// #[godot_api] - /// impl Player { - /// #[signal] - /// fn health_changed(health: i32); - /// - /// fn change_health_anim(&mut self, health: i32) { /* ... */ } - /// } - /// impl HealthUi { - /// fn on_health_changed(&mut self, health: i32) { /* ... */ } - /// } - /// #[godot_api] - /// impl INode for Player { - /// fn ready(&mut self) { - /// // Connect to this object. - /// self.signals() - /// .health_changed() - /// .connect_self(Self::change_health_anim); - /// - /// // Connect to other object, where ui: OnEditor>. - /// self.signals() - /// .health_changed() - /// .connect_self(|this, amount| this.ui.bind_mut().on_health_changed(amount)); - /// } - /// } - /// ``` pub fn connect_self(&self, mut function: F) -> ConnectHandle where for<'c_rcv> F: SignalReceiver<&'c_rcv mut C, Ps> + 'static, @@ -267,46 +268,6 @@ impl TypedSignal<'_, C, Ps> { /// /// - To connect to methods on the object that owns this signal, use [`connect_self()`][Self::connect_self]. /// - If you need [`connect flags`](ConnectFlags) or cross-thread signals, use [`builder()`][Self::builder]. - /// - /// # Example - /// ```no_run - /// # use godot::prelude::*; - /// # use godot::classes::{Button, Control, IControl}; - /// # #[derive(GodotClass)] - /// # #[class(init, base=Control)] - /// # pub struct OtherTool { - /// # base: Base, - /// # } - /// # #[derive(GodotClass)] - /// # #[class(init, base=Control)] - /// # pub struct Tool { - /// # button: OnEditor>, - /// # other_tool: OnEditor>, - /// # base: Base, - /// # } - /// impl OtherTool { - /// fn set_tool_enabled(&mut self, value: bool) { } - /// } - /// impl Tool { - /// fn execute_tool(&mut self) { } - /// } - /// #[godot_api] - /// impl IControl for Tool { - /// fn ready(&mut self) { - /// // Connect from other to self. - /// self.button - /// .signals() - /// .pressed() - /// .connect_other(self, Self::execute_tool); - /// - /// // Connect from other to other, where other_tool: OnEditor>. - /// self.button - /// .signals() - /// .pressed() - /// .connect_other(&*self.other_tool, |tool| tool.set_tool_enabled(false)); - /// } - /// } - /// ``` pub fn connect_other( &self, object: &impl ObjectToOwned,