112 lines
3.0 KiB
Rust
112 lines
3.0 KiB
Rust
use std::mem::ManuallyDrop;
|
|
|
|
use wasm_bindgen::{JsCast, intern};
|
|
use wasm_bindgen::closure::Closure;
|
|
use discard::Discard;
|
|
use web_sys::{EventTarget, Event};
|
|
|
|
use crate::bindings;
|
|
use crate::dom::EventOptions;
|
|
use crate::traits::StaticEvent;
|
|
|
|
|
|
// TODO should use gloo::events, but it doesn't support interning or Discard
|
|
pub(crate) struct EventListener {
|
|
elem: EventTarget,
|
|
name: &'static str,
|
|
capture: bool,
|
|
closure: Option<Closure<dyn FnMut(&Event)>>,
|
|
}
|
|
|
|
// TODO should these inline ?
|
|
impl EventListener {
|
|
#[inline]
|
|
pub(crate) fn new<F>(elem: EventTarget, name: &'static str, options: &EventOptions, callback: F) -> Self where F: FnMut(&Event) + 'static {
|
|
let closure = Closure::wrap(Box::new(callback) as Box<dyn FnMut(&Event)>);
|
|
let name: &'static str = intern(name);
|
|
|
|
let capture = !options.bubbles;
|
|
|
|
bindings::add_event(&elem, name, capture, !options.preventable, closure.as_ref().unchecked_ref());
|
|
|
|
Self { elem, name, capture, closure: Some(closure) }
|
|
}
|
|
|
|
#[inline]
|
|
pub(crate) fn once<F>(elem: EventTarget, name: &'static str, callback: F) -> Self where F: FnOnce(&Event) + 'static {
|
|
let closure = Closure::once(callback);
|
|
let name: &'static str = intern(name);
|
|
|
|
bindings::add_event_once(&elem, name, closure.as_ref().unchecked_ref());
|
|
|
|
Self { elem, name, capture: true, closure: Some(closure) }
|
|
}
|
|
}
|
|
|
|
impl Drop for EventListener {
|
|
#[inline]
|
|
fn drop(&mut self) {
|
|
if let Some(closure) = self.closure.take() {
|
|
// TODO can this be made more optimal ?
|
|
closure.forget();
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Discard for EventListener {
|
|
#[inline]
|
|
fn discard(mut self) {
|
|
let closure = self.closure.take().unwrap();
|
|
bindings::remove_event(&self.elem, &self.name, self.capture, closure.as_ref().unchecked_ref());
|
|
}
|
|
}
|
|
|
|
|
|
#[inline]
|
|
pub(crate) fn on<E, F>(element: EventTarget, options: &EventOptions, mut callback: F) -> EventListener
|
|
where E: StaticEvent,
|
|
F: FnMut(E) + 'static {
|
|
EventListener::new(element, E::EVENT_TYPE, options, move |e| {
|
|
callback(E::unchecked_from_event(e.clone()));
|
|
})
|
|
}
|
|
|
|
|
|
// TODO move this into the discard crate
|
|
// TODO verify that this is correct and doesn't leak memory or cause memory safety
|
|
pub(crate) struct ValueDiscard<A>(ManuallyDrop<A>);
|
|
|
|
impl<A> ValueDiscard<A> {
|
|
#[inline]
|
|
pub(crate) fn new(value: A) -> Self {
|
|
ValueDiscard(ManuallyDrop::new(value))
|
|
}
|
|
}
|
|
|
|
impl<A> Discard for ValueDiscard<A> {
|
|
#[inline]
|
|
fn discard(self) {
|
|
// TODO verify that this works
|
|
ManuallyDrop::into_inner(self.0);
|
|
}
|
|
}
|
|
|
|
|
|
// TODO move this into the discard crate
|
|
// TODO replace this with an impl for FnOnce() ?
|
|
pub(crate) struct FnDiscard<A>(A);
|
|
|
|
impl<A> FnDiscard<A> where A: FnOnce() {
|
|
#[inline]
|
|
pub(crate) fn new(f: A) -> Self {
|
|
FnDiscard(f)
|
|
}
|
|
}
|
|
|
|
impl<A> Discard for FnDiscard<A> where A: FnOnce() {
|
|
#[inline]
|
|
fn discard(self) {
|
|
self.0();
|
|
}
|
|
}
|