Adding in event_with_options and global_event_with_options

This commit is contained in:
Pauan 2021-10-17 10:31:02 +02:00
parent 6d43c4baa1
commit ca54166f10
4 changed files with 74 additions and 59 deletions

View File

@ -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);
}

View File

@ -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<A, B, C> where B: ?Sized, C: Fn(&A) -> &B {
@ -408,6 +408,38 @@ fn set_property<A, B, C>(element: &A, name: &B, value: C) where A: AsRef<JsValue
}
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
pub struct EventOptions {
pub bubbles: bool,
pub preventable: bool,
}
impl EventOptions {
pub fn bubbles() -> 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<A> {
@ -445,17 +477,19 @@ impl<A> DomBuilder<A> {
}
#[inline]
fn _event<T, F>(&mut self, element: EventTarget, listener: F)
fn _event<T, F>(&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<T, F>(&mut self, element: EventTarget, listener: F)
pub fn global_event_with_options<T, F>(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<A> DomBuilder<A> {
pub fn global_event<T, F>(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<T, F>(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<A> DomBuilder<A> where A: AsRef<JsValue> {
impl<A> DomBuilder<A> where A: AsRef<EventTarget> {
#[inline]
pub fn event<T, F>(mut self, listener: F) -> Self
pub fn event_with_options<T, F>(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<T, F>(mut self, listener: F) -> Self
pub fn event<T, F>(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<T, F>(self, listener: F) -> Self
where T: StaticEvent,
F: FnMut(T) + 'static {
self.event_with_options(&EventOptions::preventable(), listener)
}
}

View File

@ -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);

View File

@ -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<Closure<dyn FnMut(&Event)>>,
}
// TODO should these inline ?
impl EventListener {
#[inline]
pub(crate) fn new<F>(elem: EventTarget, name: &'static str, callback: F) -> Self where F: FnMut(&Event) + 'static {
pub(crate) fn new<F>(elem: EventTarget, name: &'static str, options: &EventOptions, callback: F) -> Self where F: FnMut(&Event) + 'static {
let closure = Closure::wrap(Box::new(callback) as Box<dyn FnMut(&Event)>);
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<F>(elem: EventTarget, name: &'static str, callback: F) -> Self where F: FnMut(&Event) + 'static {
let closure = Closure::wrap(Box::new(callback) as Box<dyn FnMut(&Event)>);
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<E, F>(element: EventTarget, mut callback: F) -> EventListener
pub(crate) fn on<E, F>(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<E, F>(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()));
})
}