use std::{fmt, ptr};
use godot_ffi as sys;
use sys::{ExtVariantType, GodotFfi, ffi_methods};
use crate::builtin::{Array, Callable, StringName, VarDictionary, Variant, inner};
use crate::classes::Object;
use crate::classes::object::ConnectFlags;
use crate::global::Error;
use crate::meta;
use crate::meta::{FromGodot, GodotType, ToGodot};
use crate::obj::bounds::DynMemory;
use crate::obj::{Bounds, EngineBitfield, Gd, GodotClass, InstanceId};
pub struct Signal {
opaque: sys::types::OpaqueSignal,
}
impl Signal {
fn from_opaque(opaque: sys::types::OpaqueSignal) -> Self {
Self { opaque }
}
pub fn from_object_signal<T, S>(object: &Gd<T>, signal_name: S) -> Self
where
T: GodotClass,
S: meta::AsArg<StringName>,
{
meta::arg_into_ref!(signal_name);
unsafe {
Self::new_with_uninit(|self_ptr| {
let ctor = sys::builtin_fn!(signal_from_object_signal);
let raw = object.to_ffi();
let args = [raw.as_arg_ptr(), signal_name.sys()];
ctor(self_ptr, args.as_ptr());
})
}
}
pub fn invalid() -> Self {
unsafe {
Self::new_with_uninit(|self_ptr| {
let ctor = sys::builtin_fn!(signal_construct_default);
ctor(self_ptr, ptr::null_mut())
})
}
}
pub fn connect(&self, callable: &Callable) -> Error {
let error = self.as_inner().connect(callable, 0i64);
Error::from_godot(error as i32)
}
pub fn connect_flags(&self, callable: &Callable, flags: ConnectFlags) -> Error {
let error = self.as_inner().connect(callable, flags.ord() as i64);
Error::from_godot(error as i32)
}
pub fn disconnect(&self, callable: &Callable) {
self.as_inner().disconnect(callable);
}
pub fn emit(&self, varargs: &[Variant]) {
let Some(mut object) = self.object() else {
return;
};
object.emit_signal(&self.name(), varargs);
}
pub fn connections(&self) -> Array<VarDictionary> {
self.as_inner()
.get_connections()
.iter_shared()
.map(|variant| variant.to())
.collect()
}
pub fn name(&self) -> StringName {
self.as_inner().get_name()
}
pub fn object(&self) -> Option<Gd<Object>> {
self.as_inner().get_object().map(|mut object| {
<Object as Bounds>::DynMemory::maybe_inc_ref(&mut object.raw);
object
})
}
pub fn object_id(&self) -> Option<InstanceId> {
let id = self.as_inner().get_object_id();
InstanceId::try_from_i64(id)
}
pub fn is_connected(&self, callable: &Callable) -> bool {
self.as_inner().is_connected(callable)
}
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 {
const VARIANT_TYPE: ExtVariantType = ExtVariantType::Concrete(sys::VariantType::SIGNAL);
ffi_methods! { type sys::GDExtensionTypePtr = *mut Opaque;
fn new_from_sys;
fn new_with_uninit;
fn from_arg_ptr;
fn sys;
fn sys_mut;
fn move_return_ptr;
}
unsafe fn new_with_init(init_fn: impl FnOnce(sys::GDExtensionTypePtr)) -> Self {
let mut result = Self::invalid();
init_fn(result.sys_mut());
result
}
}
impl_builtin_traits! {
for Signal {
Clone => signal_construct_copy;
Drop => signal_destroy;
PartialEq => signal_operator_equal;
}
}
meta::impl_godot_as_self!(Signal: ByRef);
impl fmt::Debug for Signal {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let name = self.name();
let object = self.object();
f.debug_struct("signal")
.field("name", &name)
.field("object", &object)
.finish()
}
}
impl fmt::Display for Signal {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.to_variant())
}
}