use crate::event_detail::EventDetail;
use crate::node_id::NodeId;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum EventPhase {
None,
Capturing,
AtTarget,
Bubbling,
}
#[derive(Debug)]
#[non_exhaustive]
pub struct Event {
pub event_type: String,
pub bubbles: bool,
pub cancelable: bool,
pub target: Option<NodeId>,
pub current_target: Option<NodeId>,
pub phase: EventPhase,
pub detail: EventDetail,
pub(crate) is_synthetic: bool,
pub(crate) propagation_stopped: bool,
pub(crate) immediate_propagation_stopped: bool,
pub(crate) default_prevented: bool,
pub(crate) dispatching: bool,
pub(crate) redraw_requested: bool,
}
impl Clone for Event {
fn clone(&self) -> Self {
let mut copy = Event::new(self.event_type.clone())
.with_bubbles(self.bubbles)
.with_cancelable(self.cancelable);
copy.detail = self.detail.clone();
copy
}
}
impl Event {
pub fn new(event_type: impl Into<String>) -> Self {
Self {
event_type: event_type.into(),
bubbles: true,
cancelable: true,
target: None,
current_target: None,
phase: EventPhase::None,
detail: EventDetail::None,
is_synthetic: false,
propagation_stopped: false,
immediate_propagation_stopped: false,
default_prevented: false,
dispatching: false,
redraw_requested: false,
}
}
pub fn with_bubbles(mut self, bubbles: bool) -> Self {
self.bubbles = bubbles;
self
}
pub fn with_cancelable(mut self, cancelable: bool) -> Self {
self.cancelable = cancelable;
self
}
pub fn with_detail(mut self, detail: impl Into<String>) -> Self {
self.detail = EventDetail::String(detail.into());
self
}
pub fn with_synthetic(mut self, synthetic: bool) -> Self {
self.is_synthetic = synthetic;
self
}
pub fn is_synthetic(&self) -> bool {
self.is_synthetic
}
pub fn stop_propagation(&mut self) {
self.propagation_stopped = true;
}
pub fn stop_immediate_propagation(&mut self) {
self.propagation_stopped = true;
self.immediate_propagation_stopped = true;
}
pub fn prevent_default(&mut self) {
if self.cancelable {
self.default_prevented = true;
}
}
pub fn redraw_requested(&self) -> bool {
self.redraw_requested
}
pub fn is_propagation_stopped(&self) -> bool {
self.propagation_stopped
}
pub fn is_immediate_propagation_stopped(&self) -> bool {
self.immediate_propagation_stopped
}
pub fn default_prevented(&self) -> bool {
self.default_prevented
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn event_defaults() {
let e = Event::new("click");
assert_eq!(e.event_type, "click");
assert!(e.bubbles);
assert!(e.cancelable);
assert_eq!(e.phase, EventPhase::None);
assert!(!e.is_propagation_stopped());
assert!(!e.default_prevented());
}
#[test]
fn stop_propagation_sets_flag() {
let mut e = Event::new("click");
e.stop_propagation();
assert!(e.is_propagation_stopped());
assert!(!e.is_immediate_propagation_stopped());
}
#[test]
fn stop_immediate_sets_both_flags() {
let mut e = Event::new("click");
e.stop_immediate_propagation();
assert!(e.is_propagation_stopped());
assert!(e.is_immediate_propagation_stopped());
}
#[test]
fn prevent_default_only_when_cancelable() {
let mut e = Event::new("click");
e.prevent_default();
assert!(e.default_prevented());
let mut e2 = Event::new("click").with_cancelable(false);
e2.prevent_default();
assert!(!e2.default_prevented());
}
#[test]
fn synthetic_default_is_false() {
let e = Event::new("click");
assert!(!e.is_synthetic());
}
#[test]
fn with_synthetic_sets_flag() {
let e = Event::new("click").with_synthetic(true);
assert!(e.is_synthetic());
let e2 = Event::new("click").with_synthetic(false);
assert!(!e2.is_synthetic());
}
#[test]
fn synthetic_flag_independent_of_other_state() {
let mut e = Event::new("click")
.with_synthetic(true)
.with_bubbles(false)
.with_cancelable(false);
e.stop_propagation();
e.prevent_default();
assert!(e.is_synthetic());
assert!(!e.bubbles);
assert!(!e.cancelable);
assert!(e.is_propagation_stopped());
assert!(!e.default_prevented()); }
use crate::dispatch::ListenerOptions;
use crate::{Dom, DomError, EventDetail};
use std::cell::RefCell;
use std::rc::Rc;
#[test]
fn clone_taken_mid_dispatch_is_a_fresh_undispatched_event() {
let mut dom: Dom = Dom::new();
let root = dom.root();
let b = dom.create_element("b");
dom.append_child(root, b).unwrap();
let stash = Rc::new(RefCell::new(Vec::new()));
{
let stash = stash.clone();
dom.add_event_listener(b, "ping", ListenerOptions::default(), move |ctx| {
ctx.event.prevent_default();
ctx.event.stop_immediate_propagation();
ctx.request_redraw();
stash.borrow_mut().push(ctx.event.clone());
})
.unwrap();
}
let mut e = Event::new("ping")
.with_bubbles(false)
.with_cancelable(true)
.with_detail("payload");
dom.dispatch_event(b, &mut e).unwrap();
let copy = stash.borrow_mut().pop().expect("listener ran");
assert_eq!(copy.event_type, "ping");
assert!(!copy.bubbles);
assert!(copy.cancelable);
assert_eq!(copy.detail, EventDetail::String("payload".into()));
assert_eq!(copy.phase, EventPhase::None);
assert_eq!(copy.target, None);
assert_eq!(copy.current_target, None);
assert!(
!copy.default_prevented(),
"a copy of a canceled event is not canceled"
);
assert!(!copy.is_propagation_stopped());
assert!(!copy.is_immediate_propagation_stopped());
assert!(!copy.redraw_requested());
}
#[test]
fn clone_dispatches_while_the_original_in_flight_still_cannot() {
let mut dom: Dom = Dom::new();
let root = dom.root();
let a = dom.create_element("a");
dom.append_child(root, a).unwrap();
let results = Rc::new(RefCell::new(Vec::new()));
let fired = Rc::new(RefCell::new(Vec::new()));
{
let fired = fired.clone();
dom.add_event_listener(a, "ping", ListenerOptions::default(), move |ctx| {
fired.borrow_mut().push(ctx.event.detail.clone());
})
.unwrap();
}
{
let results = results.clone();
dom.add_event_listener(a, "ping", ListenerOptions::default(), move |ctx| {
if ctx.event.detail == EventDetail::String("outer".into()) {
let original = ctx.dom.dispatch_event(a, ctx.event);
let mut copy = ctx.event.clone();
copy.detail = EventDetail::String("copy".into());
let nested = ctx.dom.dispatch_event(a, &mut copy);
results.borrow_mut().push((original, nested, copy));
}
})
.unwrap();
}
let mut e = Event::new("ping").with_detail("outer");
dom.dispatch_event(a, &mut e).unwrap();
let (original, nested, mut copy) = results.borrow_mut().pop().unwrap();
assert!(matches!(original, Err(DomError::InvalidState(_))));
assert_eq!(nested, Ok(()));
assert_eq!(
*fired.borrow(),
vec![
EventDetail::String("outer".into()),
EventDetail::String("copy".into())
]
);
dom.dispatch_event(a, &mut copy).unwrap();
assert_eq!(
fired.borrow().len(),
3,
"the copy dispatches again afterwards"
);
}
#[test]
fn clone_is_not_synthetic() {
let e = Event::new("click").with_synthetic(true);
assert!(!e.clone().is_synthetic());
}
}