use super::CustomSignalHub;
use crate::signal::{ConnectionHandle, GenericSignal, Signal1};
use alloc::boxed::Box;
use alloc::sync::Arc;
#[derive(Default)]
pub struct EventSignalBinder {
hub: Option<Arc<CustomSignalHub>>,
forwards: alloc::vec::Vec<Forwarded>,
}
impl EventSignalBinder {
pub fn new(hub: Arc<CustomSignalHub>) -> Self {
Self { hub: Some(hub), forwards: alloc::vec::Vec::new() }
}
pub fn detached() -> Self {
Self { hub: None, forwards: alloc::vec::Vec::new() }
}
pub fn is_attached(&self) -> bool {
self.hub.is_some()
}
pub fn len(&self) -> usize {
self.forwards.len()
}
pub fn is_empty(&self) -> bool {
self.forwards.is_empty()
}
pub fn forward_unit(&mut self, event_name: &str, signal: &GenericSignal) {
let Some(hub) = self.hub.clone() else {
return;
};
let name = alloc::string::String::from(event_name);
let handle = signal.connect(move || hub.emit(&name));
self.forwards.push(Forwarded { source: ForwardSource::Unit(signal.clone()), handle });
}
pub fn forward_all<W>(&mut self, widget: &W) -> usize
where
W: crate::widget::Widget,
{
#[cfg(full_widgets)]
{
let factory = crate::widget::capability::WidgetFactory::new_with_defaults();
let Some(capability) = factory.capability_for_kind_instance(widget) else {
return 0;
};
let mut wired = 0usize;
for schema in capability.events {
if self.wire_one(widget, schema.name) {
wired += 1;
}
}
wired
}
#[cfg(not(full_widgets))]
{
let _ = widget;
0
}
}
pub fn forward_one<W>(&mut self, widget: &W, event_name: &str) -> bool
where
W: crate::widget::Widget,
{
self.wire_one(widget, event_name)
}
fn wire_one<W>(&mut self, widget: &W, event_name: &str) -> bool
where
W: crate::widget::Widget,
{
let Some(reference) = widget.event_signal_dyn(event_name) else {
return false;
};
let Some(hub) = self.hub.clone() else {
return false;
};
let name = alloc::string::String::from(event_name);
let handle = reference.subscribe(alloc::boxed::Box::new(move |_value| hub.emit(&name)));
let source = widget.event_signal_dyn(event_name);
self.forwards.push(Forwarded {
source: ForwardSource::Erased(alloc::boxed::Box::new(move |handle| {
let _ = source.as_ref().is_some_and(|reference| reference.disconnect(handle));
})),
handle,
});
true
}
pub fn event_is_wired<W>(&self, widget: &W, event_name: &str) -> bool
where
W: crate::widget::Widget,
{
match widget.event_signal_dyn(event_name) {
Some(reference) => reference.slot_count() > 0,
None => false,
}
}
pub fn forward_widget_events<W>(&mut self, widget: &W) -> usize
where
W: crate::widget::Widget,
{
#[cfg(full_widgets)]
{
let factory = crate::widget::capability::WidgetFactory::new_with_defaults();
let Some(capability) = factory.capability_for_kind_instance(widget) else {
return 0;
};
if !capability.events.iter().any(|schema| schema.name == "clicked") {
return 0;
}
self.forward_unit("clicked", widget.clicked_signal());
1
}
#[cfg(not(full_widgets))]
{
let _ = widget;
0
}
}
pub fn forward_mapped<T, F>(&mut self, event_name: &str, signal: &Signal1<T>, mut observe: F)
where
T: Clone + Send + 'static,
F: FnMut(&T) + Send + Sync + 'static,
{
let Some(hub) = self.hub.clone() else {
return;
};
let name = alloc::string::String::from(event_name);
let handle = signal.connect(move |value: Arc<T>| {
observe(&value);
hub.emit(&name);
});
let source = signal.clone();
self.forwards.push(Forwarded {
source: ForwardSource::Typed(Box::new(move |handle| {
source.disconnect(handle);
})),
handle,
});
}
pub fn unbind_all(&mut self) {
for entry in core::mem::take(&mut self.forwards) {
entry.source.disconnect(entry.handle);
}
}
}
enum ForwardSource {
Unit(GenericSignal),
Typed(Box<dyn Fn(ConnectionHandle) + Send>),
Erased(Box<dyn Fn(ConnectionHandle) + Send>),
}
impl ForwardSource {
fn disconnect(&self, handle: ConnectionHandle) {
match self {
Self::Unit(signal) => {
signal.disconnect(handle);
}
Self::Typed(disconnect) => disconnect(handle),
Self::Erased(disconnect) => disconnect(handle),
}
}
}
struct Forwarded {
source: ForwardSource,
handle: ConnectionHandle,
}
impl core::fmt::Debug for EventSignalBinder {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("EventSignalBinder")
.field("attached", &self.is_attached())
.field("subscriptions", &self.forwards.len())
.finish()
}
}
impl Drop for EventSignalBinder {
fn drop(&mut self) {
self.unbind_all();
}
}