Skip to content

Commit

Permalink
Add SignalBuilder in place of manual Signal construction
Browse files Browse the repository at this point in the history
  • Loading branch information
Bromeon committed Dec 13, 2021
1 parent 846a3b2 commit e3c4dee
Show file tree
Hide file tree
Showing 12 changed files with 125 additions and 64 deletions.
5 changes: 1 addition & 4 deletions examples/dodge_the_creeps/src/hud.rs
Expand Up @@ -9,10 +9,7 @@ pub struct Hud;
#[methods]
impl Hud {
fn register_hud(builder: &ClassBuilder<Self>) {
builder.add_signal(Signal {
name: "start_game",
args: &[],
});
builder.signal("start_game").done();
}

fn new(_owner: &CanvasLayer) -> Self {
Expand Down
5 changes: 1 addition & 4 deletions examples/dodge_the_creeps/src/player.rs
Expand Up @@ -16,10 +16,7 @@ pub struct Player {
#[methods]
impl Player {
fn register_player(builder: &ClassBuilder<Self>) {
builder.add_signal(Signal {
name: "hit",
args: &[],
});
builder.signal("hit").done()
}

fn new(_owner: &Area2D) -> Self {
Expand Down
17 changes: 7 additions & 10 deletions examples/signals/src/lib.rs
Expand Up @@ -12,21 +12,18 @@ struct SignalEmitter {
#[methods]
impl SignalEmitter {
fn register_signals(builder: &ClassBuilder<Self>) {
builder.add_signal(Signal {
name: "tick",
args: &[],
});
builder.signal("tick").done();

builder.add_signal(Signal {
name: "tick_with_data",
builder
.signal("tick_with_data")
// Argument list used by the editor for GUI and generation of GDScript handlers. It can be omitted if the signal is only used from code.
args: &[SignalArgument {
name: "data",
.arg(SignalArgument {
name: "data".into(),
default: Variant::new(100),
export_info: ExportInfo::new(VariantType::I64),
usage: PropertyUsage::DEFAULT,
}],
});
})
.done();
}

fn new(_owner: &Node) -> Self {
Expand Down
21 changes: 9 additions & 12 deletions gdnative-async/src/rt/func_state.rs
Expand Up @@ -2,8 +2,8 @@ use gdnative_bindings::Reference;
use gdnative_core::core_types::{ToVariant, Variant, VariantType};
use gdnative_core::export::user_data::{LocalCellData, Map, MapMut};
use gdnative_core::export::{
ClassBuilder, ExportInfo, NativeClass, NativeClassMethods, PropertyUsage, Signal,
SignalArgument, StaticArgs, StaticArgsMethod,
ClassBuilder, ExportInfo, NativeClass, NativeClassMethods, PropertyUsage, SignalArgument,
StaticArgs, StaticArgsMethod,
};
use gdnative_core::godot_site;
use gdnative_core::object::ownership::Unique;
Expand Down Expand Up @@ -32,20 +32,17 @@ impl NativeClass for FuncState {
}

fn register_properties(builder: &ClassBuilder<Self>) {
builder.add_signal(Signal {
name: "completed",
args: &[SignalArgument {
name: "value",
builder
.signal("completed")
.arg(SignalArgument {
name: "value".into(),
default: Variant::nil(),
export_info: ExportInfo::new(VariantType::Nil),
usage: PropertyUsage::DEFAULT,
}],
});
})
.done();

builder.add_signal(Signal {
name: "resumable",
args: &[],
});
builder.signal("resumable").done();
}
}

Expand Down
71 changes: 47 additions & 24 deletions gdnative-core/src/export/class_builder.rs
Expand Up @@ -34,7 +34,6 @@ use std::ffi::CString;
use std::marker::PhantomData;
use std::ptr;

use crate::core_types::{GodotString, Variant};
use crate::export::*;
use crate::object::NewRef;
use crate::private::get_api;
Expand Down Expand Up @@ -141,37 +140,73 @@ impl<C: NativeClass> ClassBuilder<C> {
PropertyBuilder::new(self, name)
}

/// Returns a `SignalBuilder` which can be used to add a signal to the class being
/// registered.
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// use gdnative::prelude::*;
///
/// #[derive(NativeClass)]
/// #[inherit(Node)]
/// #[register_with(Self::my_register)]
/// #[no_constructor]
/// struct MyType {}
///
/// // Note: no #[methods] required
/// impl MyType {
/// fn my_register(builder: &ClassBuilder<MyType>) {
/// builder
/// .signal("sig")
/// .arg(SignalArgument {
/// name: "some_i64".into(),
/// default: Variant::new(100),
/// export_info: ExportInfo::new(VariantType::I64),
/// usage: PropertyUsage::DEFAULT,
/// })
/// .done();
/// }
/// }
/// ```
#[inline]
pub fn signal<'a>(&'a self, name: &'a str) -> SignalBuilder<'a, C> {
SignalBuilder::new(self, name)
}

#[inline]
pub fn add_signal(&self, signal: Signal) {
pub(crate) fn add_signal(&self, signal: Signal) {
unsafe {
let name = GodotString::from_str(signal.name);
let owned = signal
let args_and_hints = signal
.args
.iter()
.map(|arg| {
let arg_name = GodotString::from_str(arg.name);
let hint_string = arg.export_info.hint_string.new_ref();
(arg, arg_name, hint_string)
(arg, hint_string)
})
.collect::<Vec<_>>();
let mut args = owned

let mut sys_args = args_and_hints
.iter()
.map(|(arg, arg_name, hint_string)| sys::godot_signal_argument {
name: arg_name.to_sys(),
.map(|(arg, hint_string)| sys::godot_signal_argument {
name: arg.name.to_sys(),
type_: arg.default.get_type() as i32,
hint: arg.export_info.hint_kind,
hint_string: hint_string.to_sys(),
usage: arg.usage.to_sys(),
default_value: arg.default.to_sys(),
})
.collect::<Vec<_>>();

(get_api().godot_nativescript_register_signal)(
self.init_handle,
self.class_name.as_ptr(),
&sys::godot_signal {
name: name.to_sys(),
num_args: args.len() as i32,
args: args.as_mut_ptr(),
name: signal.name.to_sys(),
num_args: sys_args.len() as i32,
args: sys_args.as_mut_ptr(),
num_default_args: 0,
default_args: ptr::null_mut(),
},
Expand Down Expand Up @@ -211,15 +246,3 @@ impl<C: NativeClass> ClassBuilder<C> {
}
}
}

pub struct Signal<'l> {
pub name: &'l str,
pub args: &'l [SignalArgument<'l>],
}

pub struct SignalArgument<'l> {
pub name: &'l str,
pub default: Variant,
pub export_info: ExportInfo,
pub usage: PropertyUsage,
}
2 changes: 1 addition & 1 deletion gdnative-core/src/export/method.rs
Expand Up @@ -13,7 +13,7 @@ use crate::object::{Ref, TInstance, TRef};

/// Builder type used to register a method on a `NativeClass`.
pub struct MethodBuilder<'a, C, F> {
class_builder: &'a super::ClassBuilder<C>,
class_builder: &'a ClassBuilder<C>,
name: &'a str,
method: F,

Expand Down
2 changes: 2 additions & 0 deletions gdnative-core/src/export/mod.rs
Expand Up @@ -14,6 +14,7 @@ mod class_builder;
mod macros;
mod method;
mod property;
mod signal;

pub(crate) mod class_registry;
pub(crate) mod emplace;
Expand All @@ -26,3 +27,4 @@ pub use class::*;
pub use class_builder::*;
pub use method::*;
pub use property::*;
pub use signal::*;
2 changes: 2 additions & 0 deletions gdnative-core/src/export/property.rs
Expand Up @@ -161,6 +161,8 @@ where

/// Provides a setter function with the signature `fn(&C, owner: C::Base, value: T)`
/// where `C` is the `NativeClass` type being registered and `T` is the type of the property.
///
/// "shr" stands for "shared reference", as opposed to the more common `&mut self`.
#[inline]
pub fn with_shr_setter<NS>(
self,
Expand Down
46 changes: 46 additions & 0 deletions gdnative-core/src/export/signal.rs
@@ -0,0 +1,46 @@
use crate::core_types::{GodotString, Variant};
use crate::export::{ClassBuilder, ExportInfo, NativeClass, PropertyUsage};

pub struct SignalBuilder<'a, C> {
class_builder: &'a ClassBuilder<C>,
name: GodotString,
args: Vec<SignalArgument>,
}

impl<'a, C: NativeClass> SignalBuilder<'a, C> {
pub(super) fn new(class_builder: &'a ClassBuilder<C>, name: &str) -> Self {
Self {
class_builder,
name: GodotString::from(name),
args: vec![],
}
}

/// Add an argument for the signal.
#[inline]
pub fn arg(mut self, argument: SignalArgument) -> Self {
self.args.push(argument);
self
}

/// Finish registering the signal.
#[inline]
pub fn done(self) {
self.class_builder.add_signal(Signal {
name: self.name,
args: self.args,
});
}
}

pub(crate) struct Signal {
pub name: GodotString,
pub args: Vec<SignalArgument>,
}

pub struct SignalArgument {
pub name: GodotString,
pub default: Variant,
pub export_info: ExportInfo,
pub usage: PropertyUsage,
}
4 changes: 2 additions & 2 deletions gdnative-derive/src/lib.rs
Expand Up @@ -176,8 +176,8 @@ pub fn profiled(meta: TokenStream, input: TokenStream) -> TokenStream {
/// Self {}
/// }
/// fn my_register_function(builder: &ClassBuilder<Foo>) {
/// builder.add_signal(Signal { name: "foo", args: &[] });
/// builder.property::<f32>("bar")
/// builder.signal("my_sig").done();
/// builder.property::<f32>("my_prop")
/// .with_getter(|_, _| 42.0)
/// .with_hint(FloatHint::Range(RangeHint::new(0.0, 100.0)))
/// .done();
Expand Down
2 changes: 1 addition & 1 deletion gdnative/src/prelude.rs
Expand Up @@ -17,7 +17,7 @@ pub use gdnative_core::core_types::{
};
pub use gdnative_core::export::{
ClassBuilder, ExportInfo, Method, MethodBuilder, NativeClass, NativeClassMethods,
PropertyUsage, Signal, SignalArgument,
PropertyUsage, SignalArgument, SignalBuilder,
};
pub use gdnative_core::init::InitHandle;
pub use gdnative_core::object::{
Expand Down
12 changes: 6 additions & 6 deletions test/src/test_register.rs
Expand Up @@ -31,15 +31,15 @@ impl NativeClass for RegisterSignal {
RegisterSignal
}
fn register_properties(builder: &ClassBuilder<Self>) {
builder.add_signal(Signal {
name: "progress",
args: &[SignalArgument {
name: "amount",
builder
.signal("progress")
.arg(SignalArgument {
name: "amount".into(),
default: Variant::nil(),
export_info: ExportInfo::new(VariantType::I64),
usage: PropertyUsage::DEFAULT,
}],
});
})
.done();
}
}

Expand Down

0 comments on commit e3c4dee

Please sign in to comment.