Skip to content

Commit

Permalink
Implement builtin type Signal
Browse files Browse the repository at this point in the history
  • Loading branch information
TitanNano committed Jan 17, 2024
1 parent 52628a0 commit c8b2e98
Show file tree
Hide file tree
Showing 4 changed files with 194 additions and 55 deletions.
28 changes: 0 additions & 28 deletions godot-core/src/builtin/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -123,34 +123,6 @@ macro_rules! impl_builtin_traits {
)
}

macro_rules! impl_builtin_stub {
// ($Class:ident, $OpaqueTy:ident $( ; )? $( $Traits:ident ),* ) => {
($Class:ident, $OpaqueTy:ident) => {
#[repr(C)]
// #[derive(Copy, Clone)]
pub struct $Class {
opaque: sys::types::$OpaqueTy,
}

impl $Class {
fn from_opaque(opaque: sys::types::$OpaqueTy) -> Self {
Self { opaque }
}
}

// SAFETY:
// This is simply a wrapper around an `Opaque` value representing a value of the type.
// So this is safe.
unsafe impl GodotFfi for $Class {
fn variant_type() -> sys::VariantType {
sys::VariantType::$Class
}

ffi_methods! { type sys::GDExtensionTypePtr = *mut Opaque; .. }
}
};
}

macro_rules! impl_builtin_froms {
($To:ty; $($From:ty => $from_fn:ident),* $(,)?) => {
$(impl From<&$From> for $To {
Expand Down
4 changes: 2 additions & 2 deletions godot-core/src/builtin/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@ pub use basis::*;
pub use callable::*;
pub use color::*;
pub use dictionary_inner::Dictionary;
pub use others::*;
pub use packed_array::*;
pub use plane::*;
pub use projection::*;
Expand All @@ -51,6 +50,7 @@ pub use real_inner::*;
pub use rect2::*;
pub use rect2i::*;
pub use rid::*;
pub use signal::*;
pub use string::*;
pub use transform2d::*;
pub use transform3d::*;
Expand Down Expand Up @@ -84,14 +84,14 @@ mod aabb;
mod basis;
mod callable;
mod color;
mod others;
mod packed_array;
mod plane;
mod projection;
mod quaternion;
mod rect2;
mod rect2i;
mod rid;
mod signal;
mod string;
mod transform2d;
mod transform3d;
Expand Down
25 changes: 0 additions & 25 deletions godot-core/src/builtin/others.rs

This file was deleted.

192 changes: 192 additions & 0 deletions godot-core/src/builtin/signal.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
/*
* Copyright (c) godot-rust; Bromeon and contributors.
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/

use std::fmt;

// Stub for various other built-in classes, which are currently incomplete, but whose types
// are required for codegen
use godot_ffi as sys;
use sys::{ffi_methods, GodotFfi};

use crate::{
builtin::meta::ToGodot,
engine::{global::Error, object::ConnectFlags, Object},
obj::{EngineEnum, Gd, GodotClass, InstanceId},
};

use super::{
inner,
meta::{impl_godot_as_self, FromGodot, GodotType},
Array, Callable, Dictionary, StringName, Variant,
};

/// A `Signal` represents a signal of an Object instance in Godot.
///
/// Signals are composed of a reference to an `Object` and the name of the signal on this object.
#[repr(C, align(8))]
pub struct Signal {
opaque: sys::types::OpaqueSignal,
}

impl Signal {
fn from_opaque(opaque: sys::types::OpaqueSignal) -> Self {
Self { opaque }
}

/// Create a signal for the signal `object::signal_name`.
///
/// _Godot equivalent: `Signal(Object object, StringName signal)`_
pub fn from_object_signal<T, S>(object: &Gd<T>, signal_name: S) -> Self
where
T: GodotClass,
S: Into<StringName>,
{
let signal = signal_name.into();
unsafe {
sys::from_sys_init_or_init_default::<Self>(|self_ptr| {
let ctor = sys::builtin_fn!(signal_from_object_signal);
let raw = object.to_ffi();
let args = [raw.as_arg_ptr(), signal.sys_const()];
ctor(self_ptr, args.as_ptr());
})
}
}

/// Connects this signal to the specified callable.
///
/// Optional flags can be also added to configure the connection's behavior (see [`ConnectFlags`](crate::engine::object::ConnectFlags) constants).
/// You can provide additional arguments to the connected callable by using `Callable::bind`.
///
/// A signal can only be connected once to the same `Callable`. If the signal is already connected,
/// returns [`Error::ERR_INVALID_PARAMETER`](crate::engine::global::Error::ERR_INVALID_PARAMETER) and
/// pushes an error message, unless the signal is connected with `ConnectFlags::CONNECT_REFERENCE_COUNTED`.
/// To prevent this, use [`Self::is_connected`](Self::is_connected) first to check for existing connections.
pub fn connect(&self, callable: Callable, flags: ConnectFlags) -> Error {
let error = self.as_inner().connect(callable, flags.ord() as i64);

Error::from_godot(error as i32)
}

/// Disconnects this signal from the specified Callable.
///
/// If the connection does not exist, generates an error. Use []`Self::is_connected`](Self::is_connected)
/// to make sure that the connection exists.
pub fn disconnect(&self, callable: Callable) {
self.as_inner().disconnect(callable);
}

/// Emits this signal.
///
/// All Callables connected to this signal will be triggered.
pub fn emit(&self, varargs: &[Variant]) {
let Some(mut object) = self.object() else {
return;
};

object.emit_signal(self.name(), varargs);
}

/// Returns an `Array` of connections for this signal.
///
/// Each connection is represented as a Dictionary that contains three entries:
/// - `signal` is a reference to this `Signal`;
/// - `callable` is a reference to the connected `Callable`;
/// - `flags` is a combination of `ConnectFlags`.
///
/// _Godot equivalent: `get_connections`_
pub fn connections(&self) -> Array<Dictionary> {
self.as_inner()
.get_connections()
.iter_shared()
.map(|variant| variant.to())
.collect()
}

/// Returns the name of the signal.
pub fn name(&self) -> StringName {
self.as_inner().get_name()
}

/// Returns the object to which this signal belongs.
///
/// Returns `None` when this signal doesn't have any object.
///
/// _Godot equivalent: `get_object`_
pub fn object(&self) -> Option<Gd<Object>> {
self.as_inner().get_object()
}

/// Returns the ID of this signal's object, see also [`Gd::instance_id`].
///
/// Returns `None` when this signal doesn't have any object.
///
/// _Godot equivalent: `get_object_id`_
pub fn object_id(&self) -> Option<InstanceId> {
let id = self.as_inner().get_object_id();
InstanceId::try_from_i64(id)
}

/// Returns `true` if the specified [`Callable`] is connected to this signal.
pub fn is_connected(&self, callable: Callable) -> bool {
self.as_inner().is_connected(callable)
}

/// Returns `true` if the signal's name does not exist in its object, or the object is not valid.
pub fn is_null(&self) -> bool {
self.as_inner().is_null()
}

#[doc(hidden)]
pub fn as_inner(&self) -> inner::InnerSignal {
inner::InnerSignal::from_outer(self)
}
}

unsafe impl GodotFfi for Signal {
fn variant_type() -> sys::VariantType {
sys::VariantType::Signal
}

ffi_methods! { type sys::GDExtensionTypePtr = *mut Opaque;
fn from_sys;
fn sys;
fn from_sys_init;
fn move_return_ptr;
}

unsafe fn from_arg_ptr(ptr: sys::GDExtensionTypePtr, _call_type: sys::PtrcallType) -> Self {
Self::from_sys(ptr)
}
}

impl_builtin_traits! {
for Signal {
Clone => signal_construct_copy;
Drop => signal_destroy;
PartialEq => signal_operator_equal;
}
}

impl_godot_as_self!(Signal);

impl fmt::Debug for Signal {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let method = self.name();
let object = self.object();

f.debug_struct("Callable")
.field("method", &method)
.field("object", &object)
.finish()
}
}

impl fmt::Display for Signal {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.to_variant())
}
}

0 comments on commit c8b2e98

Please sign in to comment.