use super::{EventRoute, IRoutedEventArgs, RouteHandler, RoutedEvent, RoutingStrategies};
use crate::metadata::MarkupDelegate;
use crate::BoxedValue;
use crate::layout::{Layoutable, LayoutableImpl};
use crate::{
ferro_class, ferro_impl_classes, instantiate, FerroObjectImpl, Ref, StyledElementImpl, VisualImpl,
};
use std::cell::{Cell, RefCell};
use std::rc::Rc;
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct RoutedEventHandlerToken(u64);
struct EventSubscription {
token: RoutedEventHandlerToken,
handler: RouteHandler,
routes: RoutingStrategies,
handled_events_too: bool,
}
struct EventSubscriptions {
routed_event: RoutedEvent,
subscriptions: Vec<EventSubscription>,
}
crate::ferro_thread_local! {
static TOTAL_HANDLERS_COUNT: Cell<usize> = const { Cell::new(0) };
static NEXT_TOKEN: Cell<u64> = const { Cell::new(1) };
}
#[repr(C)]
pub struct Interactive {
base: Layoutable,
event_handlers: RefCell<Vec<EventSubscriptions>>,
}
ferro_class! {
Interactive: Layoutable, virtuals InteractiveImpl: LayoutableImpl {
fn interactive_parent(this) -> Option<Ref<Interactive>>;
}
}
crate::ferro_class_info!(Interactive { new: Interactive::new });
ferro_impl_classes!(Interactive: FerroObjectImpl, StyledElementImpl, VisualImpl, LayoutableImpl);
impl InteractiveImpl for Interactive {
fn interactive_parent(this: &Self) -> Option<Ref<Interactive>> {
this.visual_parent().and_then(|parent| parent.downcast::<Interactive>().ok())
}
}
impl Interactive {
pub const DEFAULT_ROUTES: RoutingStrategies = RoutingStrategies::DIRECT.union(RoutingStrategies::BUBBLE);
pub fn construct() -> Self {
Self { base: Layoutable::construct(), event_handlers: RefCell::new(Vec::new()) }
}
pub fn new() -> Ref<Self> {
instantiate(Self::construct())
}
#[cfg_attr(not(test), allow(dead_code))]
pub(crate) fn total_handlers_count() -> usize {
TOTAL_HANDLERS_COUNT.get()
}
pub fn add_handler<TEventArgs: IRoutedEventArgs>(
&self,
routed_event: &RoutedEvent<TEventArgs>,
handler: impl Fn(&Interactive, &TEventArgs) + 'static,
) -> RoutedEventHandlerToken {
self.add_handler_with(routed_event, handler, Self::DEFAULT_ROUTES, false)
}
pub fn add_handler_with<TEventArgs: IRoutedEventArgs>(
&self,
routed_event: &RoutedEvent<TEventArgs>,
handler: impl Fn(&Interactive, &TEventArgs) + 'static,
routes: RoutingStrategies,
handled_events_too: bool,
) -> RoutedEventHandlerToken {
self.add_handler_as::<TEventArgs, TEventArgs>(routed_event, handler, routes, handled_events_too)
}
pub fn add_handler_as<THandlerArgs: IRoutedEventArgs, TEventArgs: ?Sized>(
&self,
routed_event: &RoutedEvent<TEventArgs>,
handler: impl Fn(&Interactive, &THandlerArgs) + 'static,
routes: RoutingStrategies,
handled_events_too: bool,
) -> RoutedEventHandlerToken {
assert!(
routed_event.event_args_is::<THandlerArgs>(),
"A handler taking {} cannot be attached to {}, whose args are {}.",
std::any::type_name::<THandlerArgs>(),
routed_event,
routed_event.event_args_type_name()
);
let adapter: RouteHandler = Rc::new(move |sender: &Interactive, args: &dyn IRoutedEventArgs| {
match args.downcast_ref::<THandlerArgs>() {
Some(typed_args) => handler(sender, typed_args),
None => panic!(
"Event args of the wrong type were raised: expected {}.",
std::any::type_name::<THandlerArgs>()
),
}
});
self.add_route_handler(routed_event, adapter, routes, handled_events_too)
}
pub fn add_handler_untyped<TEventArgs: ?Sized>(
&self,
routed_event: &RoutedEvent<TEventArgs>,
handler: MarkupDelegate,
routes: RoutingStrategies,
handled_events_too: bool,
) -> RoutedEventHandlerToken {
let adapter: RouteHandler = Rc::new(move |sender: &Interactive, args: &dyn IRoutedEventArgs| {
let sender: BoxedValue = Rc::new(sender.to_ref());
let args: BoxedValue = Rc::new(args.share());
handler.invoke(&[Some(sender), Some(args)]);
});
self.add_route_handler(routed_event, adapter, routes, handled_events_too)
}
pub fn add_route_handler<TEventArgs: ?Sized>(
&self,
routed_event: &RoutedEvent<TEventArgs>,
handler: RouteHandler,
routes: RoutingStrategies,
handled_events_too: bool,
) -> RoutedEventHandlerToken {
let token = RoutedEventHandlerToken(NEXT_TOKEN.get());
NEXT_TOKEN.set(token.0 + 1);
let subscription = EventSubscription { token, handler, routes, handled_events_too };
self.add_event_subscription(routed_event.as_routed_event(), subscription);
token
}
pub fn remove_handler<TEventArgs: ?Sized>(
&self,
routed_event: &RoutedEvent<TEventArgs>,
handler: RoutedEventHandlerToken,
) -> bool {
let routed_event = routed_event.as_routed_event();
let removed = {
let mut event_handlers = self.event_handlers.borrow_mut();
event_handlers.iter_mut().find(|entry| entry.routed_event == routed_event).and_then(|entry| {
let index = entry.subscriptions.iter().rposition(|s| s.token == handler)?;
Some(entry.subscriptions.remove(index))
})
};
if removed.is_some() {
TOTAL_HANDLERS_COUNT.set(TOTAL_HANDLERS_COUNT.get() - 1);
}
removed.is_some()
}
pub fn raise_event(&self, e: &dyn IRoutedEventArgs) {
let Some(routed_event) = e.routed_event() else {
panic!("Cannot raise an event whose RoutedEvent is null.");
};
let route = self.build_event_route(&routed_event);
route.raise_event(&self.to_ref(), e);
}
pub fn build_event_route<TEventArgs: ?Sized>(&self, e: &RoutedEvent<TEventArgs>) -> EventRoute {
let e = e.as_routed_event();
let mut result = EventRoute::new(&e);
let has_class_handlers = e.has_raised_subscriptions();
let strategies = e.routing_strategies();
let this = self.to_ref();
if strategies.contains(RoutingStrategies::BUBBLE) || strategies.contains(RoutingStrategies::TUNNEL) {
let mut element = Some(this);
while let Some(current) = element {
if has_class_handlers {
result.add_class_handler(¤t);
}
current.add_to_event_route(¤t, e, &mut result);
element = current.interactive_parent();
}
} else {
if has_class_handlers {
result.add_class_handler(&this);
}
self.add_to_event_route(&this, e, &mut result);
}
result
}
fn add_event_subscription(&self, routed_event: RoutedEvent, subscription: EventSubscription) {
let mut event_handlers = self.event_handlers.borrow_mut();
match event_handlers.iter_mut().find(|entry| entry.routed_event == routed_event) {
Some(entry) => entry.subscriptions.push(subscription),
None => event_handlers.push(EventSubscriptions { routed_event, subscriptions: vec![subscription] }),
}
TOTAL_HANDLERS_COUNT.set(TOTAL_HANDLERS_COUNT.get() + 1);
}
fn add_to_event_route(&self, this: &Ref<Interactive>, routed_event: RoutedEvent, route: &mut EventRoute) {
let event_handlers = self.event_handlers.borrow();
if let Some(entry) = event_handlers.iter().find(|entry| entry.routed_event == routed_event) {
for sub in &entry.subscriptions {
route.add(this, sub.handler.clone(), sub.routes, sub.handled_events_too);
}
}
}
}