wrend/utils/
listener.rs

1use std::{fmt::Debug, ops::Deref};
2
3use wasm_bindgen::{convert::FromWasmAbi, prelude::Closure, JsCast, JsValue};
4use web_sys::EventTarget;
5
6/// Safe wrapper around `eventListener` callbacks that cleans them up once the `Listener` struct is dropped
7/// For more information, see https://github.com/rustwasm/wasm-bindgen/issues/993.
8///
9/// This utility can be used with any type that dereferences to EventTarget, so it is not limited
10/// to just pure HtmlElements.
11#[derive(Debug)]
12pub struct Listener<Element: Deref<Target = EventTarget>, Arg: FromWasmAbi + 'static = JsValue> {
13    element: Element,
14    name: &'static str,
15    cb: Closure<dyn Fn(Arg)>,
16}
17
18impl<Element: Deref<Target = EventTarget>, Arg: FromWasmAbi + 'static> Listener<Element, Arg> {
19    /// Attaches a listener callback to an element
20    pub fn new<F>(element: Element, name: &'static str, cb: F) -> Self
21    where
22        F: Fn(Arg) + 'static,
23    {
24        let cb = Closure::wrap(Box::new(cb) as Box<dyn Fn(Arg)>);
25
26        element
27            .add_event_listener_with_callback(name, cb.as_ref().unchecked_ref())
28            .unwrap();
29        Self { element, name, cb }
30    }
31
32    /// Get the element that this listener is attached to
33    pub fn element(&self) -> &Element {
34        &self.element
35    }
36
37    /// Get the name of the event that this listener is listening for
38    pub fn name(&self) -> &'static str {
39        self.name
40    }
41
42    /// Get the callback that is called when the listener's event is emitted
43    pub fn callback(&self) -> &Closure<dyn Fn(Arg)> {
44        &self.cb
45    }
46}
47
48impl<Element: Deref<Target = EventTarget>, Arg: FromWasmAbi + 'static> Drop
49    for Listener<Element, Arg>
50{
51    fn drop(&mut self) {
52        self.element
53            .remove_event_listener_with_callback(self.name, self.cb.as_ref().unchecked_ref())
54            .unwrap();
55    }
56}