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>, } // TODO should these inline ? impl EventListener { #[inline] pub(crate) fn new(elem: EventTarget, name: &'static str, options: &EventOptions, callback: F) -> Self where F: FnMut(&Event) + 'static { let closure = Closure::wrap(Box::new(callback) as Box); 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(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(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(ManuallyDrop); impl ValueDiscard { #[inline] pub(crate) fn new(value: A) -> Self { ValueDiscard(ManuallyDrop::new(value)) } } impl Discard for ValueDiscard { #[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); impl FnDiscard where A: FnOnce() { #[inline] pub(crate) fn new(f: A) -> Self { FnDiscard(f) } } impl Discard for FnDiscard where A: FnOnce() { #[inline] fn discard(self) { self.0(); } }