simi 0.2.1

A framework for building wasm front-end web application in Rust
//! Implement handlers that send message from html elements back to a simi app

use crate::app::{Application, WeakMain};
use wasm_bindgen::closure::Closure;
use wasm_bindgen::JsCast;

/// A trait for managing html element event listeners
pub trait Listener {
    /// `click`, `input`...
    fn event_name(&self) -> &str;
    /// Get js_sys::Function
    fn js_function(&self) -> &js_sys::Function;
}

/// Provides methods to attach handlers to real html elements
pub trait EventHandler<A: Application> {
    /// Create and attach event listener to the given element
    fn create_event_listener(&mut self, main: &WeakMain<A>) -> Box<Listener>;
}

/// A type use in a component to receive handler passed from the app.
pub type ElementEventHandler<A> = ::std::cell::Cell<Option<Box<EventHandler<A>>>>;

// This may cause some confusion
// $HandlerType(s) in simi have the same names as their equivalent types in web_sys
// So in this macro, $HandlerType is used for both simi types and web_sys types
macro_rules! impl_element_events_that_send_message_back_to_simi_app {
    (
        $(($handler_doc:expr, $HandlerType:ident, $listener_doc:expr, $ListenerType:ident))+
    ) => {$(
        #[doc = $handler_doc]
        pub struct $HandlerType<F> {
            event_name: &'static str,
            handler: Option<F>,
        }

        impl<F> $HandlerType<F> {
            /// Create a new event handler from a handler
            pub fn new(event_name: &'static str, handler: F) -> Self {
                Self {
                    event_name,
                    handler: Some(handler),
                }
            }
        }

        impl<F,A> EventHandler<A> for $HandlerType<F>
        where
            A: Application,
            F: Fn(web_sys::$HandlerType) -> A::Message + 'static,
        {
            /// Create and attach event listener to the given element
            fn create_event_listener(&mut self, main: &WeakMain<A>) -> Box<Listener> { //, simi_element: &mut Element) {
                let main = main.clone();
                let handler = self.handler.take().expect("Event handler");
                let handler = move |val: web_sys::$HandlerType| {
                    main.send_message(handler(val));
                };
                let closure = Closure::wrap(Box::new(handler) as Box<Fn(web_sys::$HandlerType)>);
                Box::new(
                    $ListenerType{
                        event_name: self.event_name,
                        closure
                    }
                )
            }
        }

        #[doc = $listener_doc]
        pub(crate) struct $ListenerType {
            event_name: &'static str,
            closure: Closure<Fn(web_sys::$HandlerType)>,
        }

        impl Listener for $ListenerType {
            /// Event name of the listener
            fn event_name(&self) -> &str {
                self.event_name
            }
            /// Get js function
            fn js_function(&self) -> &js_sys::Function{
                self.closure.as_ref().unchecked_ref()
            }
        }
    )+};
}

impl_element_events_that_send_message_back_to_simi_app! {
    (
        "Event handler for FocusEvent",
        FocusEvent,
        "Event listener for FocusEvent",
        FocusEventListener
    )
    (
        "Event handler for MouseEvent",
        MouseEvent,
        "Event listener for MouseEvent",
        MouseEventListener
    )
    (
        "Event handler for WheelEvent",
        WheelEvent,
        "Event listener for WheelEvent",
        WheelEventListener
    )
    (
        "Event handler for UiEvent",
        UiEvent,
        "Event listener for UiEvent",
        UiEventListener
    )
    (
        "Event handler for InputEvent",
        InputEvent,
        "Event listener for InputEvent",
        InputEventListener
    )
    (
        "Event handler for KeyboardEvent",
        KeyboardEvent,
        "Event listener for KeyboardEvent",
        KeyboardEventListener
    )
    (
        "Event handler for Event",
        Event,
        "Event listener for Event",
        EventListener
    )
}