1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
use wasm_bindgen::closure::Closure;
use wasm_bindgen::{JsCast, UnwrapThrowExt};

pub trait Listener {}

macro_rules! create_events {
    ($($EventType:ident $EventListener:ident { $($EventName:ident => $event_name:literal,)+ })+) => {
        $(
            pub struct $EventListener {
                event_name: &'static str,
                event_target: web_sys::EventTarget,
                closure: Closure<dyn Fn(web_sys::$EventType)>,
            }
            impl $EventListener {
                // TODO: remove duplicated code here
                fn new(event_name: &'static str, event_target: &web_sys::EventTarget, closure: Closure<dyn Fn(web_sys::$EventType)>) -> Self {
                    event_target.add_event_listener_with_callback(
                        event_name,
                        closure.as_ref().unchecked_ref()
                    ).expect_throw("Expect event register to be successful");
                    Self {
                        event_name,
                        event_target: event_target.clone(),
                        closure,
                    }
                }
            }

            impl Listener for $EventListener {}

            impl Drop for $EventListener {
                #[inline]
                fn drop(&mut self) {
                    self.event_target
                        .remove_event_listener_with_callback(
                            self.event_name,
                            self.closure.as_ref().unchecked_ref()
                        ).expect_throw("Expect event removal to be successful");
                }
            }
            $(
                #[doc = "Help creating "]
                #[doc = $event_name]
                #[doc = " event listener"]
                pub trait $EventName {
                    fn on(self, node: &web_sys::EventTarget) -> Box<dyn Listener>;
                    fn on_window(self) -> Box<dyn Listener>;
                }

                impl<T> $EventName for T
                where
                    T: 'static + Fn(web_sys::$EventType),
                {
                    fn on(self, target: &web_sys::EventTarget) -> Box<dyn Listener> {
                        let closure = Closure::wrap(Box::new(self) as Box<dyn Fn(web_sys::$EventType)>);
                        Box::new($EventListener::new($event_name, target, closure))
                    }

                    fn on_window(self) -> Box<dyn Listener> {
                        $EventName::on(self, crate::utils::window().as_ref())
                    }
                }
            )+
        )+
    };
}

create_events! {
    FocusEvent FocusEventListener {
        Focus => "focus",
        Blur => "blur",
    }
    MouseEvent MouseEventListener {
        AuxClick => "auxclick",
        Click => "click",
        DblClick => "dblclick",
        DoubleClick => "dblclick",
        MouseEnter => "mouseenter",
        MouseOver => "mouseover",
        MouseMove => "mousemove",
        MouseDown => "mousedown",
        MouseUp => "mouseup",
        MouseLeave => "mouseleave",
        MouseOut => "mouseout",
        ContextMenu => "contextmenu",
    }
    WheelEvent WheelEventListener {
        Wheel => "wheel",
    }
    UiEvent UiEventListener {
        UiSelect => "select",
    }
    InputEvent InputEventListener {
        Input => "input",
    }
    KeyboardEvent KeyboardEventListener {
        KeyDown => "keydown",
        KeyPress => "keypress",
        KeyUp => "keyup",
    }
    Event EventListener {
        Change => "change",
        Reset => "reset",
        Submit => "submit",
        PointerLockChange => "pointerlockchange",
        PointerLockError => "pointerlockerror",
    }
}