use std::marker::PhantomData;
use std::ops::Deref;
use wasm_bindgen::prelude::wasm_bindgen;
use wasm_bindgen::{JsCast, JsValue};
use web_sys::{HtmlElement, HtmlInputElement};
use crate::internal::{self, In, Out};
#[wasm_bindgen]
extern "C" {
type EventWithTarget;
#[wasm_bindgen(method, getter)]
fn target(this: &EventWithTarget) -> HtmlElement;
}
macro_rules! event {
($(#[doc = $doc:literal] $event:ident,)*) => {
$(
#[doc = concat!("Smart wrapper around a ", $doc, "which includes the type information of the event target")]
#[repr(transparent)]
pub struct $event<T> {
event: web_sys::$event,
_target: PhantomData<T>,
}
impl<T> From<web_sys::Event> for $event<T> {
fn from(event: web_sys::Event) -> Self {
$event {
event: event.unchecked_into(),
_target: PhantomData,
}
}
}
impl<T> EventCast for $event<T> {}
impl<T> Deref for $event<T> {
type Target = web_sys::$event;
fn deref(&self) -> &Self::Target {
&self.event.unchecked_ref()
}
}
impl<T> $event<T> {
pub fn target(&self) -> EventTarget<T>
where
T: JsCast,
{
EventTarget(self.event.unchecked_ref::<EventWithTarget>().target().unchecked_into())
}
}
)*
};
}
mod sealed {
pub trait EventCast {}
impl EventCast for web_sys::Event {}
}
pub(crate) use sealed::EventCast;
event! {
Event,
KeyboardEvent,
MouseEvent,
}
pub trait IntoListener<E: EventCast> {
type Listener: Listener<E>;
fn into_listener(self) -> Self::Listener;
}
impl<E, L> IntoListener<E> for L
where
L: Listener<E>,
E: EventCast,
{
type Listener = L;
fn into_listener(self) -> L {
self
}
}
pub trait Listener<E>
where
E: EventCast,
Self: Sized + 'static,
{
type Product: ListenerHandle;
fn build(self, p: In<Self::Product>) -> Out<Self::Product>;
fn update(self, p: &mut Self::Product);
}
impl<E, F> Listener<E> for F
where
F: FnMut(E) + 'static,
E: EventCast,
{
type Product = ListenerProduct<Self, E>;
fn build(self, p: In<Self::Product>) -> Out<Self::Product> {
p.put(ListenerProduct {
closure: self,
_event: PhantomData,
})
}
fn update(self, p: &mut ListenerProduct<Self, E>) {
p.closure = self;
}
}
pub struct ListenerProduct<F, E> {
closure: F,
_event: PhantomData<E>,
}
pub trait ListenerHandle {
fn js(&mut self) -> JsValue;
}
impl<F, E> ListenerHandle for ListenerProduct<F, E>
where
F: FnMut(E) + 'static,
E: EventCast,
{
fn js(&mut self) -> JsValue {
let vcall: fn(E, *mut ()) = |e, ptr| unsafe { (*(ptr as *mut F))(e) };
internal::make_event_handler((&mut self.closure) as *mut F as *mut (), vcall as usize)
}
}
#[repr(transparent)]
pub struct EventTarget<T>(T);
impl<T> Deref for EventTarget<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl EventTarget<HtmlInputElement> {
pub fn focus(&self) {
drop(self.0.focus());
}
}