diff --git a/src/bindings.rs b/src/bindings.rs index 6e66ad2..b0e643f 100644 --- a/src/bindings.rs +++ b/src/bindings.rs @@ -7,32 +7,24 @@ use web_sys::{HtmlElement, Element, Node, Window, History, Document, Text, Comme #[wasm_bindgen(inline_js = " export function set_property(obj, name, value) { obj[name] = value; } - export function add_event(elem, name, f) { + export function add_event(elem, name, capture, passive, f) { elem.addEventListener(name, f, { - capture: true, + capture, + passive, once: false, - passive: true }); } export function add_event_once(elem, name, f) { elem.addEventListener(name, f, { capture: true, - once: true, passive: true, + once: true, }); } - export function add_event_preventable(elem, name, f) { - elem.addEventListener(name, f, { - capture: true, - once: false, - passive: false - }); - } - - export function remove_event(elem, name, f) { - elem.removeEventListener(name, f, true); + export function remove_event(elem, name, capture, f) { + elem.removeEventListener(name, f, capture); } ")] extern "C" { @@ -41,10 +33,9 @@ extern "C" { pub(crate) fn set_property(obj: &JsValue, name: &str, value: &JsValue); // TODO replace with gloo-events - pub(crate) fn add_event(elem: &EventTarget, name: &str, f: &Function); + pub(crate) fn add_event(elem: &EventTarget, name: &str, capture: bool, passive: bool, f: &Function); pub(crate) fn add_event_once(elem: &EventTarget, name: &str, f: &Function); - pub(crate) fn add_event_preventable(elem: &EventTarget, name: &str, f: &Function); - pub(crate) fn remove_event(elem: &EventTarget, name: &str, f: &Function); + pub(crate) fn remove_event(elem: &EventTarget, name: &str, capture: bool, f: &Function); } diff --git a/src/dom.rs b/src/dom.rs index eba5afa..e623bea 100644 --- a/src/dom.rs +++ b/src/dom.rs @@ -18,7 +18,7 @@ use crate::callbacks::Callbacks; use crate::traits::*; use crate::operations; use crate::operations::{for_each, spawn_future}; -use crate::utils::{EventListener, on, on_preventable, ValueDiscard, FnDiscard}; +use crate::utils::{EventListener, on, ValueDiscard, FnDiscard}; pub struct RefFn where B: ?Sized, C: Fn(&A) -> &B { @@ -408,6 +408,38 @@ fn set_property(element: &A, name: &B, value: C) where A: AsRef Self { + Self { + bubbles: true, + preventable: false, + } + } + + pub fn preventable() -> Self { + Self { + bubbles: false, + preventable: true, + } + } +} + +impl Default for EventOptions { + fn default() -> Self { + Self { + bubbles: false, + preventable: false, + } + } +} + + // TODO better warning message for must_use #[must_use] pub struct DomBuilder { @@ -445,17 +477,19 @@ impl DomBuilder { } #[inline] - fn _event(&mut self, element: EventTarget, listener: F) + fn _event(&mut self, element: EventTarget, options: &EventOptions, listener: F) where T: StaticEvent, F: FnMut(T) + 'static { - self.callbacks.after_remove(on(element, listener)); + self.callbacks.after_remove(on(element, options, listener)); } + // TODO add this to the StylesheetBuilder and ClassBuilder too #[inline] - fn _event_preventable(&mut self, element: EventTarget, listener: F) + pub fn global_event_with_options(mut self, options: &EventOptions, listener: F) -> Self where T: StaticEvent, F: FnMut(T) + 'static { - self.callbacks.after_remove(on_preventable(element, listener)); + self._event(bindings::window_event_target(), options, listener); + self } // TODO add this to the StylesheetBuilder and ClassBuilder too @@ -463,17 +497,16 @@ impl DomBuilder { pub fn global_event(mut self, listener: F) -> Self where T: StaticEvent, F: FnMut(T) + 'static { - self._event(bindings::window_event_target(), listener); - self + self.global_event_with_options(&EventOptions::default(), listener) } // TODO add this to the StylesheetBuilder and ClassBuilder too + #[deprecated(since = "0.5.21", note = "Use global_event_with_options instead")] #[inline] pub fn global_event_preventable(mut self, listener: F) -> Self where T: StaticEvent, F: FnMut(T) + 'static { - self._event_preventable(bindings::window_event_target(), listener); - self + self.global_event_with_options(&EventOptions::preventable(), listener) } #[inline] @@ -598,21 +631,27 @@ impl DomBuilder where A: AsRef { impl DomBuilder where A: AsRef { #[inline] - pub fn event(mut self, listener: F) -> Self + pub fn event_with_options(mut self, options: &EventOptions, listener: F) -> Self where T: StaticEvent, F: FnMut(T) + 'static { // TODO can this clone be avoided ? - self._event(self.element.as_ref().clone(), listener); + self._event(self.element.as_ref().clone(), options, listener); self } #[inline] - pub fn event_preventable(mut self, listener: F) -> Self + pub fn event(self, listener: F) -> Self where T: StaticEvent, F: FnMut(T) + 'static { - // TODO can this clone be avoided ? - self._event_preventable(self.element.as_ref().clone(), listener); - self + self.event_with_options(&EventOptions::default(), listener) + } + + #[deprecated(since = "0.5.21", note = "Use event_with_options instead")] + #[inline] + pub fn event_preventable(self, listener: F) -> Self + where T: StaticEvent, + F: FnMut(T) + 'static { + self.event_with_options(&EventOptions::preventable(), listener) } } diff --git a/src/routing.rs b/src/routing.rs index a72b4fe..68f950a 100644 --- a/src/routing.rs +++ b/src/routing.rs @@ -5,7 +5,7 @@ use once_cell::sync::Lazy; use futures_signals::signal::{Mutable, ReadOnlyMutable}; use crate::bindings; -use crate::dom::{Dom, DomBuilder}; +use crate::dom::{Dom, DomBuilder, EventOptions}; use crate::utils::EventListener; use crate::events; @@ -34,7 +34,7 @@ impl CurrentUrl { let value = Mutable::new(String::from(bindings::current_url())); // TODO clean this up somehow ? - let _ = EventListener::new(bindings::window_event_target(), "popstate", { + let _ = EventListener::new(bindings::window_event_target(), "popstate", &EventOptions::default(), { let value = value.clone(); move |_| { change_url(&value); diff --git a/src/utils.rs b/src/utils.rs index f723283..3e662bd 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -6,6 +6,7 @@ use discard::Discard; use web_sys::{EventTarget, Event}; use crate::bindings; +use crate::dom::EventOptions; use crate::traits::StaticEvent; @@ -13,29 +14,22 @@ use crate::traits::StaticEvent; 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, callback: F) -> Self where F: FnMut(&Event) + 'static { + 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); - bindings::add_event(&elem, name, closure.as_ref().unchecked_ref()); + let capture = !options.bubbles; - Self { elem, name, closure: Some(closure) } - } + bindings::add_event(&elem, name, capture, !options.preventable, closure.as_ref().unchecked_ref()); - #[inline] - pub(crate) fn new_preventable(elem: EventTarget, name: &'static str, callback: F) -> Self where F: FnMut(&Event) + 'static { - let closure = Closure::wrap(Box::new(callback) as Box); - let name: &'static str = intern(name); - - bindings::add_event_preventable(&elem, name, closure.as_ref().unchecked_ref()); - - Self { elem, name, closure: Some(closure) } + Self { elem, name, capture, closure: Some(closure) } } #[inline] @@ -45,7 +39,7 @@ impl EventListener { bindings::add_event_once(&elem, name, closure.as_ref().unchecked_ref()); - Self { elem, name, closure: Some(closure) } + Self { elem, name, capture: true, closure: Some(closure) } } } @@ -63,25 +57,16 @@ impl Discard for EventListener { #[inline] fn discard(mut self) { let closure = self.closure.take().unwrap_throw(); - bindings::remove_event(&self.elem, &self.name, closure.as_ref().unchecked_ref()); + bindings::remove_event(&self.elem, &self.name, self.capture, closure.as_ref().unchecked_ref()); } } #[inline] -pub(crate) fn on(element: EventTarget, mut callback: F) -> EventListener +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, move |e| { - callback(E::unchecked_from_event(e.clone())); - }) -} - -#[inline] -pub(crate) fn on_preventable(element: EventTarget, mut callback: F) -> EventListener - where E: StaticEvent, - F: FnMut(E) + 'static { - EventListener::new_preventable(element, E::EVENT_TYPE, move |e| { + EventListener::new(element, E::EVENT_TYPE, options, move |e| { callback(E::unchecked_from_event(e.clone())); }) }