feat: events bubble now
This commit is contained in:
1 parent
b6262edd53
commit
f2234068ba
6 files changed
+63
-57
No files matched your search
@@ -12,6 +12,7 @@ use std::{
|
|||||||
any::Any,
|
any::Any,
|
||||||
cell::{Cell, RefCell},
|
cell::{Cell, RefCell},
|
||||||
fmt::{Arguments, Debug, Formatter},
|
fmt::{Arguments, Debug, Formatter},
|
||||||
|
sync::Arc,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// A composable "VirtualNode" to declare a User Interface in the Dioxus VirtualDOM.
|
/// A composable "VirtualNode" to declare a User Interface in the Dioxus VirtualDOM.
|
||||||
@@ -366,7 +367,8 @@ pub struct Listener<'bump> {
|
|||||||
pub event: &'static str,
|
pub event: &'static str,
|
||||||
|
|
||||||
/// The actual callback that the user specified
|
/// The actual callback that the user specified
|
||||||
pub(crate) callback: RefCell<Option<BumpBox<'bump, dyn FnMut(Box<dyn Any + Send>) + 'bump>>>,
|
pub(crate) callback:
|
||||||
|
RefCell<Option<BumpBox<'bump, dyn FnMut(std::sync::Arc<dyn Any + Send + Sync>) + 'bump>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Virtual Components for custom user-defined components
|
/// Virtual Components for custom user-defined components
|
||||||
@@ -643,7 +645,7 @@ impl<'a> NodeFactory<'a> {
|
|||||||
pub fn listener(
|
pub fn listener(
|
||||||
self,
|
self,
|
||||||
event: &'static str,
|
event: &'static str,
|
||||||
callback: BumpBox<'a, dyn FnMut(Box<dyn Any + Send>) + 'a>,
|
callback: BumpBox<'a, dyn FnMut(Arc<dyn Any + Send + Sync>) + 'a>,
|
||||||
) -> Listener<'a> {
|
) -> Listener<'a> {
|
||||||
Listener {
|
Listener {
|
||||||
mounted_node: Cell::new(None),
|
mounted_node: Cell::new(None),
|
||||||
|
|||||||
@@ -449,30 +449,6 @@ impl Scope {
|
|||||||
self.generation.set(self.generation.get() + 1);
|
self.generation.set(self.generation.get() + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A safe wrapper around calling listeners
|
|
||||||
pub(crate) fn call_listener(&self, event: UserEvent, element: ElementId) {
|
|
||||||
let listners = &mut self.items.borrow_mut().listeners;
|
|
||||||
|
|
||||||
let listener = listners.iter().find(|lis| {
|
|
||||||
let search = lis;
|
|
||||||
if search.event == event.name {
|
|
||||||
let search_id = search.mounted_node.get();
|
|
||||||
search_id.map(|f| f == element).unwrap_or(false)
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if let Some(listener) = listener {
|
|
||||||
let mut cb = listener.callback.borrow_mut();
|
|
||||||
if let Some(cb) = cb.as_mut() {
|
|
||||||
(cb)(event.event);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
log::warn!("An event was triggered but there was no listener to handle it");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// General strategy here is to load up the appropriate suspended task and then run it.
|
// General strategy here is to load up the appropriate suspended task and then run it.
|
||||||
// Suspended nodes cannot be called repeatedly.
|
// Suspended nodes cannot be called repeatedly.
|
||||||
pub(crate) fn call_suspended_node<'a>(&'a mut self, task_id: u64) {
|
pub(crate) fn call_suspended_node<'a>(&'a mut self, task_id: u64) {
|
||||||
|
|||||||
@@ -2,7 +2,10 @@ use bumpalo::Bump;
|
|||||||
use futures_channel::mpsc::UnboundedSender;
|
use futures_channel::mpsc::UnboundedSender;
|
||||||
use fxhash::FxHashMap;
|
use fxhash::FxHashMap;
|
||||||
use slab::Slab;
|
use slab::Slab;
|
||||||
use std::cell::{Cell, RefCell};
|
use std::{
|
||||||
|
borrow::Borrow,
|
||||||
|
cell::{Cell, RefCell},
|
||||||
|
};
|
||||||
|
|
||||||
use crate::innerlude::*;
|
use crate::innerlude::*;
|
||||||
|
|
||||||
@@ -361,6 +364,28 @@ impl ScopeArena {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn call_listener_with_bubbling(&self, event: UserEvent, element: ElementId) {
|
||||||
|
let nodes = self.nodes.borrow();
|
||||||
|
let mut cur_el = Some(element);
|
||||||
|
|
||||||
|
while let Some(id) = cur_el.take() {
|
||||||
|
if let Some(el) = nodes.get(id.0) {
|
||||||
|
let real_el = unsafe { &**el };
|
||||||
|
if let VNode::Element(real_el) = real_el {
|
||||||
|
for listener in real_el.listeners.borrow().iter() {
|
||||||
|
if listener.event == event.name {
|
||||||
|
let mut cb = listener.callback.borrow_mut();
|
||||||
|
if let Some(cb) = cb.as_mut() {
|
||||||
|
(cb)(event.event.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cur_el = real_el.parent_id.get();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// The head of the bumpframe is the first linked NodeLink
|
// The head of the bumpframe is the first linked NodeLink
|
||||||
pub fn wip_head(&self, id: &ScopeId) -> &VNode {
|
pub fn wip_head(&self, id: &ScopeId) -> &VNode {
|
||||||
let scope = self.get_scope(id).unwrap();
|
let scope = self.get_scope(id).unwrap();
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ use futures_util::{Future, StreamExt};
|
|||||||
use fxhash::FxHashSet;
|
use fxhash::FxHashSet;
|
||||||
use indexmap::IndexSet;
|
use indexmap::IndexSet;
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
|
use std::rc::Rc;
|
||||||
|
use std::sync::Arc;
|
||||||
use std::task::Poll;
|
use std::task::Poll;
|
||||||
use std::{any::Any, collections::VecDeque};
|
use std::{any::Any, collections::VecDeque};
|
||||||
|
|
||||||
@@ -402,9 +404,7 @@ impl VirtualDom {
|
|||||||
log::info!("Calling listener {:?}, {:?}", event.scope_id, element);
|
log::info!("Calling listener {:?}, {:?}", event.scope_id, element);
|
||||||
|
|
||||||
if let Some(scope) = self.scopes.get_scope(&event.scope_id) {
|
if let Some(scope) = self.scopes.get_scope(&event.scope_id) {
|
||||||
// TODO: bubble properly here
|
self.scopes.call_listener_with_bubbling(event, element);
|
||||||
scope.call_listener(event, element);
|
|
||||||
|
|
||||||
while let Ok(Some(dirty_scope)) = self.receiver.try_next() {
|
while let Ok(Some(dirty_scope)) = self.receiver.try_next() {
|
||||||
self.pending_messages.push_front(dirty_scope);
|
self.pending_messages.push_front(dirty_scope);
|
||||||
}
|
}
|
||||||
@@ -644,7 +644,7 @@ pub struct UserEvent {
|
|||||||
pub name: &'static str,
|
pub name: &'static str,
|
||||||
|
|
||||||
/// Event Data
|
/// Event Data
|
||||||
pub event: Box<dyn Any + Send>,
|
pub event: Arc<dyn Any + Send + Sync>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Priority of Event Triggers.
|
/// Priority of Event Triggers.
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ use std::any::Any;
|
|||||||
|
|
||||||
pub mod on {
|
pub mod on {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use std::sync::Arc;
|
||||||
macro_rules! event_directory {
|
macro_rules! event_directory {
|
||||||
( $(
|
( $(
|
||||||
$( #[$attr:meta] )*
|
$( #[$attr:meta] )*
|
||||||
@@ -23,19 +24,19 @@ pub mod on {
|
|||||||
c: NodeFactory<'a>,
|
c: NodeFactory<'a>,
|
||||||
mut callback: F,
|
mut callback: F,
|
||||||
) -> Listener<'a>
|
) -> Listener<'a>
|
||||||
where F: FnMut($wrapper) + 'a
|
where F: FnMut(&$wrapper) + 'a
|
||||||
{
|
{
|
||||||
let bump = &c.bump();
|
let bump = &c.bump();
|
||||||
|
|
||||||
// we can't allocate unsized in bumpalo's box, so we need to craft the box manually
|
// we can't allocate unsized in bumpalo's box, so we need to craft the box manually
|
||||||
// safety: this is essentially the same as calling Box::new() but manually
|
// safety: this is essentially the same as calling Box::new() but manually
|
||||||
// The box is attached to the lifetime of the bumpalo allocator
|
// The box is attached to the lifetime of the bumpalo allocator
|
||||||
let cb: &mut dyn FnMut(Box<dyn Any + Send>) = bump.alloc(move |evt: Box<dyn Any + Send>| {
|
let cb: &mut dyn FnMut(Arc<dyn Any + Send + Sync>) = bump.alloc(move |evt: Arc<dyn Any + Send + Sync>| {
|
||||||
let event = evt.downcast::<$wrapper>().unwrap();
|
let event = evt.downcast_ref::<$wrapper>().unwrap();
|
||||||
callback(*event)
|
callback(event)
|
||||||
});
|
});
|
||||||
|
|
||||||
let callback: BumpBox<dyn FnMut(Box<dyn Any + Send>) + 'a> = unsafe { BumpBox::from_raw(cb) };
|
let callback: BumpBox<dyn FnMut(Arc<dyn Any + Send + Sync>) + 'a> = unsafe { BumpBox::from_raw(cb) };
|
||||||
|
|
||||||
// ie oncopy
|
// ie oncopy
|
||||||
let event_name = stringify!($name);
|
let event_name = stringify!($name);
|
||||||
|
|||||||
+23
-21
@@ -9,7 +9,7 @@
|
|||||||
|
|
||||||
use dioxus_core::{DomEdit, ElementId, SchedulerMsg, ScopeId, UserEvent};
|
use dioxus_core::{DomEdit, ElementId, SchedulerMsg, ScopeId, UserEvent};
|
||||||
use fxhash::FxHashMap;
|
use fxhash::FxHashMap;
|
||||||
use std::{any::Any, fmt::Debug, rc::Rc};
|
use std::{any::Any, fmt::Debug, rc::Rc, sync::Arc};
|
||||||
use wasm_bindgen::{closure::Closure, JsCast};
|
use wasm_bindgen::{closure::Closure, JsCast};
|
||||||
use web_sys::{
|
use web_sys::{
|
||||||
CssStyleDeclaration, Document, Element, Event, HtmlElement, HtmlInputElement,
|
CssStyleDeclaration, Document, Element, Event, HtmlElement, HtmlInputElement,
|
||||||
@@ -92,7 +92,7 @@ impl WebsysDom {
|
|||||||
// .as_ref()
|
// .as_ref()
|
||||||
// .unwrap()
|
// .unwrap()
|
||||||
// .clone();
|
// .clone();
|
||||||
// bla.set(Box::new(node)).unwrap();
|
// bla.set(Arc::new(node)).unwrap();
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
@@ -286,14 +286,16 @@ impl WebsysDom {
|
|||||||
} else {
|
} else {
|
||||||
let trigger = self.sender_callback.clone();
|
let trigger = self.sender_callback.clone();
|
||||||
|
|
||||||
let handler = Closure::wrap(Box::new(move |event: &web_sys::Event| {
|
let c: Box<dyn FnMut(&Event)> = Box::new(move |event: &web_sys::Event| {
|
||||||
// "Result" cannot be received from JS
|
// "Result" cannot be received from JS
|
||||||
// Instead, we just build and immediately execute a closure that returns result
|
// Instead, we just build and immediately execute a closure that returns result
|
||||||
match decode_trigger(event) {
|
match decode_trigger(event) {
|
||||||
Ok(synthetic_event) => trigger.as_ref()(SchedulerMsg::UiEvent(synthetic_event)),
|
Ok(synthetic_event) => trigger.as_ref()(SchedulerMsg::UiEvent(synthetic_event)),
|
||||||
Err(e) => log::error!("Error decoding Dioxus event attribute. {:#?}", e),
|
Err(e) => log::error!("Error decoding Dioxus event attribute. {:#?}", e),
|
||||||
};
|
};
|
||||||
}) as Box<dyn FnMut(&Event)>);
|
});
|
||||||
|
|
||||||
|
let handler = Closure::wrap(c);
|
||||||
|
|
||||||
self.root
|
self.root
|
||||||
.add_event_listener_with_callback(event, (&handler).as_ref().unchecked_ref())
|
.add_event_listener_with_callback(event, (&handler).as_ref().unchecked_ref())
|
||||||
@@ -483,21 +485,21 @@ unsafe impl Sync for DioxusWebsysEvent {}
|
|||||||
|
|
||||||
// todo: some of these events are being casted to the wrong event type.
|
// todo: some of these events are being casted to the wrong event type.
|
||||||
// We need tests that simulate clicks/etc and make sure every event type works.
|
// We need tests that simulate clicks/etc and make sure every event type works.
|
||||||
fn virtual_event_from_websys_event(event: web_sys::Event) -> Box<dyn Any + Send> {
|
fn virtual_event_from_websys_event(event: web_sys::Event) -> Arc<dyn Any + Send + Sync> {
|
||||||
use dioxus_html::on::*;
|
use dioxus_html::on::*;
|
||||||
use dioxus_html::KeyCode;
|
use dioxus_html::KeyCode;
|
||||||
// use dioxus_core::events::on::*;
|
// use dioxus_core::events::on::*;
|
||||||
match event.type_().as_str() {
|
match event.type_().as_str() {
|
||||||
"copy" | "cut" | "paste" => Box::new(ClipboardEvent {}),
|
"copy" | "cut" | "paste" => Arc::new(ClipboardEvent {}),
|
||||||
"compositionend" | "compositionstart" | "compositionupdate" => {
|
"compositionend" | "compositionstart" | "compositionupdate" => {
|
||||||
let evt: &web_sys::CompositionEvent = event.dyn_ref().unwrap();
|
let evt: &web_sys::CompositionEvent = event.dyn_ref().unwrap();
|
||||||
Box::new(CompositionEvent {
|
Arc::new(CompositionEvent {
|
||||||
data: evt.data().unwrap_or_default(),
|
data: evt.data().unwrap_or_default(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
"keydown" | "keypress" | "keyup" => {
|
"keydown" | "keypress" | "keyup" => {
|
||||||
let evt: &web_sys::KeyboardEvent = event.dyn_ref().unwrap();
|
let evt: &web_sys::KeyboardEvent = event.dyn_ref().unwrap();
|
||||||
Box::new(KeyboardEvent {
|
Arc::new(KeyboardEvent {
|
||||||
alt_key: evt.alt_key(),
|
alt_key: evt.alt_key(),
|
||||||
char_code: evt.char_code(),
|
char_code: evt.char_code(),
|
||||||
key: evt.key(),
|
key: evt.key(),
|
||||||
@@ -513,7 +515,7 @@ fn virtual_event_from_websys_event(event: web_sys::Event) -> Box<dyn Any + Send>
|
|||||||
}
|
}
|
||||||
"focus" | "blur" => {
|
"focus" | "blur" => {
|
||||||
//
|
//
|
||||||
Box::new(FocusEvent {})
|
Arc::new(FocusEvent {})
|
||||||
}
|
}
|
||||||
// "change" => SyntheticEvent::GenericEvent(DioxusEvent::new((), DioxusWebsysEvent(event))),
|
// "change" => SyntheticEvent::GenericEvent(DioxusEvent::new((), DioxusWebsysEvent(event))),
|
||||||
|
|
||||||
@@ -558,13 +560,13 @@ fn virtual_event_from_websys_event(event: web_sys::Event) -> Box<dyn Any + Send>
|
|||||||
})
|
})
|
||||||
.expect("only an InputElement or TextAreaElement or an element with contenteditable=true can have an oninput event listener");
|
.expect("only an InputElement or TextAreaElement or an element with contenteditable=true can have an oninput event listener");
|
||||||
|
|
||||||
Box::new(FormEvent { value })
|
Arc::new(FormEvent { value })
|
||||||
}
|
}
|
||||||
"click" | "contextmenu" | "doubleclick" | "drag" | "dragend" | "dragenter" | "dragexit"
|
"click" | "contextmenu" | "doubleclick" | "drag" | "dragend" | "dragenter" | "dragexit"
|
||||||
| "dragleave" | "dragover" | "dragstart" | "drop" | "mousedown" | "mouseenter"
|
| "dragleave" | "dragover" | "dragstart" | "drop" | "mousedown" | "mouseenter"
|
||||||
| "mouseleave" | "mousemove" | "mouseout" | "mouseover" | "mouseup" => {
|
| "mouseleave" | "mousemove" | "mouseout" | "mouseover" | "mouseup" => {
|
||||||
let evt: &web_sys::MouseEvent = event.dyn_ref().unwrap();
|
let evt: &web_sys::MouseEvent = event.dyn_ref().unwrap();
|
||||||
Box::new(MouseEvent {
|
Arc::new(MouseEvent {
|
||||||
alt_key: evt.alt_key(),
|
alt_key: evt.alt_key(),
|
||||||
button: evt.button(),
|
button: evt.button(),
|
||||||
buttons: evt.buttons(),
|
buttons: evt.buttons(),
|
||||||
@@ -582,7 +584,7 @@ fn virtual_event_from_websys_event(event: web_sys::Event) -> Box<dyn Any + Send>
|
|||||||
"pointerdown" | "pointermove" | "pointerup" | "pointercancel" | "gotpointercapture"
|
"pointerdown" | "pointermove" | "pointerup" | "pointercancel" | "gotpointercapture"
|
||||||
| "lostpointercapture" | "pointerenter" | "pointerleave" | "pointerover" | "pointerout" => {
|
| "lostpointercapture" | "pointerenter" | "pointerleave" | "pointerover" | "pointerout" => {
|
||||||
let evt: &web_sys::PointerEvent = event.dyn_ref().unwrap();
|
let evt: &web_sys::PointerEvent = event.dyn_ref().unwrap();
|
||||||
Box::new(PointerEvent {
|
Arc::new(PointerEvent {
|
||||||
alt_key: evt.alt_key(),
|
alt_key: evt.alt_key(),
|
||||||
button: evt.button(),
|
button: evt.button(),
|
||||||
buttons: evt.buttons(),
|
buttons: evt.buttons(),
|
||||||
@@ -608,11 +610,11 @@ fn virtual_event_from_websys_event(event: web_sys::Event) -> Box<dyn Any + Send>
|
|||||||
// get_modifier_state: evt.get_modifier_state(),
|
// get_modifier_state: evt.get_modifier_state(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
"select" => Box::new(SelectionEvent {}),
|
"select" => Arc::new(SelectionEvent {}),
|
||||||
|
|
||||||
"touchcancel" | "touchend" | "touchmove" | "touchstart" => {
|
"touchcancel" | "touchend" | "touchmove" | "touchstart" => {
|
||||||
let evt: &web_sys::TouchEvent = event.dyn_ref().unwrap();
|
let evt: &web_sys::TouchEvent = event.dyn_ref().unwrap();
|
||||||
Box::new(TouchEvent {
|
Arc::new(TouchEvent {
|
||||||
alt_key: evt.alt_key(),
|
alt_key: evt.alt_key(),
|
||||||
ctrl_key: evt.ctrl_key(),
|
ctrl_key: evt.ctrl_key(),
|
||||||
meta_key: evt.meta_key(),
|
meta_key: evt.meta_key(),
|
||||||
@@ -620,11 +622,11 @@ fn virtual_event_from_websys_event(event: web_sys::Event) -> Box<dyn Any + Send>
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
"scroll" => Box::new(()),
|
"scroll" => Arc::new(()),
|
||||||
|
|
||||||
"wheel" => {
|
"wheel" => {
|
||||||
let evt: &web_sys::WheelEvent = event.dyn_ref().unwrap();
|
let evt: &web_sys::WheelEvent = event.dyn_ref().unwrap();
|
||||||
Box::new(WheelEvent {
|
Arc::new(WheelEvent {
|
||||||
delta_x: evt.delta_x(),
|
delta_x: evt.delta_x(),
|
||||||
delta_y: evt.delta_y(),
|
delta_y: evt.delta_y(),
|
||||||
delta_z: evt.delta_z(),
|
delta_z: evt.delta_z(),
|
||||||
@@ -634,7 +636,7 @@ fn virtual_event_from_websys_event(event: web_sys::Event) -> Box<dyn Any + Send>
|
|||||||
|
|
||||||
"animationstart" | "animationend" | "animationiteration" => {
|
"animationstart" | "animationend" | "animationiteration" => {
|
||||||
let evt: &web_sys::AnimationEvent = event.dyn_ref().unwrap();
|
let evt: &web_sys::AnimationEvent = event.dyn_ref().unwrap();
|
||||||
Box::new(AnimationEvent {
|
Arc::new(AnimationEvent {
|
||||||
elapsed_time: evt.elapsed_time(),
|
elapsed_time: evt.elapsed_time(),
|
||||||
animation_name: evt.animation_name(),
|
animation_name: evt.animation_name(),
|
||||||
pseudo_element: evt.pseudo_element(),
|
pseudo_element: evt.pseudo_element(),
|
||||||
@@ -643,7 +645,7 @@ fn virtual_event_from_websys_event(event: web_sys::Event) -> Box<dyn Any + Send>
|
|||||||
|
|
||||||
"transitionend" => {
|
"transitionend" => {
|
||||||
let evt: &web_sys::TransitionEvent = event.dyn_ref().unwrap();
|
let evt: &web_sys::TransitionEvent = event.dyn_ref().unwrap();
|
||||||
Box::new(TransitionEvent {
|
Arc::new(TransitionEvent {
|
||||||
elapsed_time: evt.elapsed_time(),
|
elapsed_time: evt.elapsed_time(),
|
||||||
property_name: evt.property_name(),
|
property_name: evt.property_name(),
|
||||||
pseudo_element: evt.pseudo_element(),
|
pseudo_element: evt.pseudo_element(),
|
||||||
@@ -655,15 +657,15 @@ fn virtual_event_from_websys_event(event: web_sys::Event) -> Box<dyn Any + Send>
|
|||||||
| "playing" | "progress" | "ratechange" | "seeked" | "seeking" | "stalled" | "suspend"
|
| "playing" | "progress" | "ratechange" | "seeked" | "seeking" | "stalled" | "suspend"
|
||||||
| "timeupdate" | "volumechange" | "waiting" => {
|
| "timeupdate" | "volumechange" | "waiting" => {
|
||||||
//
|
//
|
||||||
Box::new(MediaEvent {})
|
Arc::new(MediaEvent {})
|
||||||
}
|
}
|
||||||
|
|
||||||
"toggle" => {
|
"toggle" => {
|
||||||
//
|
//
|
||||||
Box::new(ToggleEvent {})
|
Arc::new(ToggleEvent {})
|
||||||
}
|
}
|
||||||
|
|
||||||
_ => Box::new(()),
|
_ => Arc::new(()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user