wip: overhaul event system
This commit is contained in:
1 parent
c44bd11fe5
commit
47d0f51e00
48 files changed
+3175
-2597
No files matched your search
@@ -1,5 +1,6 @@
|
||||
use crate::{nodes::VNode, virtual_dom::VirtualDom};
|
||||
|
||||
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
|
||||
pub struct ElementId(pub usize);
|
||||
|
||||
|
||||
@@ -87,10 +87,14 @@ impl VirtualDom {
|
||||
value,
|
||||
id,
|
||||
}),
|
||||
AttributeValue::Bool(value) => mutations.push(SetBoolAttribute {
|
||||
name: attribute.name,
|
||||
value,
|
||||
id,
|
||||
}),
|
||||
AttributeValue::Listener(_) => todo!("create listener attributes"),
|
||||
AttributeValue::Float(_) => todo!(),
|
||||
AttributeValue::Int(_) => todo!(),
|
||||
AttributeValue::Bool(_) => todo!(),
|
||||
AttributeValue::Any(_) => todo!(),
|
||||
AttributeValue::None => todo!(),
|
||||
}
|
||||
@@ -138,11 +142,13 @@ impl VirtualDom {
|
||||
TemplateNode::DynamicText { .. } => mutations.push(CreateText {
|
||||
value: "placeholder",
|
||||
}),
|
||||
|
||||
TemplateNode::Element {
|
||||
attrs,
|
||||
children,
|
||||
namespace,
|
||||
tag,
|
||||
inner_opt,
|
||||
} => {
|
||||
let id = self.next_element(template);
|
||||
|
||||
@@ -159,6 +165,10 @@ impl VirtualDom {
|
||||
_ => None,
|
||||
}));
|
||||
|
||||
if children.is_empty() && inner_opt {
|
||||
return;
|
||||
}
|
||||
|
||||
children
|
||||
.into_iter()
|
||||
.for_each(|child| self.create_static_node(mutations, template, child));
|
||||
@@ -176,7 +186,7 @@ impl VirtualDom {
|
||||
idx: usize,
|
||||
) -> usize {
|
||||
match &node {
|
||||
DynamicNode::Text { id, value } => {
|
||||
DynamicNode::Text { id, value, inner } => {
|
||||
let new_id = self.next_element(template);
|
||||
id.set(new_id);
|
||||
mutations.push(HydrateText {
|
||||
@@ -188,11 +198,17 @@ impl VirtualDom {
|
||||
}
|
||||
|
||||
DynamicNode::Component {
|
||||
props, placeholder, ..
|
||||
props,
|
||||
placeholder,
|
||||
scope: scope_slot,
|
||||
..
|
||||
} => {
|
||||
let scope = self
|
||||
.new_scope(unsafe { std::mem::transmute(props.get()) })
|
||||
.id;
|
||||
|
||||
scope_slot.set(Some(scope));
|
||||
|
||||
let return_nodes = unsafe { self.run_scope(scope).extend_lifetime_ref() };
|
||||
|
||||
match return_nodes {
|
||||
@@ -247,7 +263,7 @@ impl VirtualDom {
|
||||
}
|
||||
}
|
||||
|
||||
DynamicNode::Fragment(children) => children
|
||||
DynamicNode::Fragment { nodes, .. } => nodes
|
||||
.iter()
|
||||
.fold(0, |acc, child| acc + self.create(mutations, child)),
|
||||
|
||||
|
||||
@@ -177,7 +177,7 @@ impl<'b> VirtualDom {
|
||||
todo!()
|
||||
},
|
||||
|
||||
(DynamicNode::Text { id: lid, value: lvalue }, DynamicNode::Text { id: rid, value: rvalue }) => {
|
||||
(DynamicNode::Text { id: lid, value: lvalue, .. }, DynamicNode::Text { id: rid, value: rvalue, .. }) => {
|
||||
rid.set(lid.get());
|
||||
if lvalue != rvalue {
|
||||
muts.push(Mutation::SetText {
|
||||
@@ -196,7 +196,7 @@ impl<'b> VirtualDom {
|
||||
(DynamicNode::Placeholder(_), _) => todo!(),
|
||||
|
||||
|
||||
(DynamicNode::Fragment (l), DynamicNode::Fragment (r)) => {
|
||||
(DynamicNode::Fragment { nodes: lnodes, ..}, DynamicNode::Fragment { nodes: rnodes, ..}) => {
|
||||
|
||||
|
||||
// match (old, new) {
|
||||
|
||||
+61
-47
@@ -1,50 +1,5 @@
|
||||
use crate::{arena::ElementId, virtual_dom::VirtualDom, Attribute, AttributeValue};
|
||||
use std::cell::Cell;
|
||||
|
||||
/// User Events are events that are shuttled from the renderer into the [`VirtualDom`] through the scheduler channel.
|
||||
///
|
||||
/// These events will be passed to the appropriate Element given by `mounted_dom_id` and then bubbled up through the tree
|
||||
/// where each listener is checked and fired if the event name matches.
|
||||
///
|
||||
/// It is the expectation that the event name matches the corresponding event listener, otherwise Dioxus will panic in
|
||||
/// attempting to downcast the event data.
|
||||
///
|
||||
/// Because Event Data is sent across threads, it must be `Send + Sync`. We are hoping to lift the `Sync` restriction but
|
||||
/// `Send` will not be lifted. The entire `UserEvent` must also be `Send + Sync` due to its use in the scheduler channel.
|
||||
///
|
||||
/// # Example
|
||||
/// ```rust, ignore
|
||||
/// fn App(cx: Scope) -> Element {
|
||||
/// render!(div {
|
||||
/// onclick: move |_| println!("Clicked!")
|
||||
/// })
|
||||
/// }
|
||||
///
|
||||
/// let mut dom = VirtualDom::new(App);
|
||||
/// let mut scheduler = dom.get_scheduler_channel();
|
||||
/// scheduler.unbounded_send(SchedulerMsg::UiEvent(
|
||||
/// UserEvent {
|
||||
/// scope_id: None,
|
||||
/// priority: EventPriority::Medium,
|
||||
/// name: "click",
|
||||
/// element: Some(ElementId(0)),
|
||||
/// data: Arc::new(ClickEvent { .. })
|
||||
/// }
|
||||
/// )).unwrap();
|
||||
/// ```
|
||||
#[derive(Debug)]
|
||||
pub struct UiEvent<T> {
|
||||
/// The priority of the event to be scheduled around ongoing work
|
||||
pub priority: EventPriority,
|
||||
|
||||
/// The optional real node associated with the trigger
|
||||
pub element: ElementId,
|
||||
|
||||
/// The event type IE "onclick" or "onmouseover"
|
||||
pub name: &'static str,
|
||||
pub bubble: Cell<bool>,
|
||||
pub event: T,
|
||||
}
|
||||
use bumpalo::boxed::Box as BumpBox;
|
||||
use std::{any::Any, cell::RefCell};
|
||||
|
||||
/// Priority of Event Triggers.
|
||||
///
|
||||
@@ -108,6 +63,65 @@ pub fn is_path_ascendant(small: &[u8], big: &[u8]) -> bool {
|
||||
small == &big[..small.len()]
|
||||
}
|
||||
|
||||
pub type InternalHandler<'bump> = &'bump RefCell<Option<InternalListenerCallback<'bump>>>;
|
||||
type InternalListenerCallback<'bump> = BumpBox<'bump, dyn FnMut(&'bump dyn Any) + 'bump>;
|
||||
type ExternalListenerCallback<'bump, T> = BumpBox<'bump, dyn FnMut(T) + 'bump>;
|
||||
|
||||
/// The callback type generated by the `rsx!` macro when an `on` field is specified for components.
|
||||
///
|
||||
/// This makes it possible to pass `move |evt| {}` style closures into components as property fields.
|
||||
///
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust, ignore
|
||||
///
|
||||
/// rsx!{
|
||||
/// MyComponent { onclick: move |evt| log::info!("clicked"), }
|
||||
/// }
|
||||
///
|
||||
/// #[derive(Props)]
|
||||
/// struct MyProps<'a> {
|
||||
/// onclick: EventHandler<'a, MouseEvent>,
|
||||
/// }
|
||||
///
|
||||
/// fn MyComponent(cx: Scope<'a, MyProps<'a>>) -> Element {
|
||||
/// cx.render(rsx!{
|
||||
/// button {
|
||||
/// onclick: move |evt| cx.props.onclick.call(evt),
|
||||
/// }
|
||||
/// })
|
||||
/// }
|
||||
///
|
||||
/// ```
|
||||
pub struct EventHandler<'bump, T = ()> {
|
||||
/// The (optional) callback that the user specified
|
||||
/// Uses a `RefCell` to allow for interior mutability, and FnMut closures.
|
||||
pub callback: RefCell<Option<ExternalListenerCallback<'bump, T>>>,
|
||||
}
|
||||
|
||||
impl<'a, T> Default for EventHandler<'a, T> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
callback: RefCell::new(None),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> EventHandler<'_, T> {
|
||||
/// Call this event handler with the appropriate event type
|
||||
pub fn call(&self, event: T) {
|
||||
if let Some(callback) = self.callback.borrow_mut().as_mut() {
|
||||
callback(event);
|
||||
}
|
||||
}
|
||||
|
||||
/// Forcibly drop the internal handler callback, releasing memory
|
||||
pub fn release(&self) {
|
||||
self.callback.replace(None);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn matches_slice() {
|
||||
let left = &[1, 2, 3];
|
||||
|
||||
+101
-33
@@ -18,6 +18,7 @@ impl ScopeState {
|
||||
DynamicNode::Text {
|
||||
id: Cell::new(ElementId(0)),
|
||||
value: text,
|
||||
inner: false,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,27 +38,32 @@ impl ScopeState {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn fragment_from_iter<'a, I, F: IntoVnode<'a, I>>(
|
||||
pub fn fragment_from_iter<'a, 'c, I, J>(
|
||||
&'a self,
|
||||
it: impl IntoIterator<Item = F>,
|
||||
node_iter: impl IntoVNode<'a, I, J> + 'c,
|
||||
) -> DynamicNode {
|
||||
let mut bump_vec = bumpalo::vec![in self.bump();];
|
||||
node_iter.into_vnode(self)
|
||||
|
||||
for item in it {
|
||||
bump_vec.push(item.into_dynamic_node(self));
|
||||
}
|
||||
// let mut bump_vec = bumpalo::vec![in self.bump();];
|
||||
|
||||
match bump_vec.len() {
|
||||
0 => DynamicNode::Placeholder(Cell::new(ElementId(0))),
|
||||
_ => DynamicNode::Fragment(bump_vec.into_bump_slice()),
|
||||
}
|
||||
// for item in it {
|
||||
// bump_vec.push(item.into_vnode(self));
|
||||
// }
|
||||
|
||||
// match bump_vec.len() {
|
||||
// 0 => DynamicNode::Placeholder(Cell::new(ElementId(0))),
|
||||
// _ => DynamicNode::Fragment {
|
||||
// inner: false,
|
||||
// nodes: bump_vec.into_bump_slice(),
|
||||
// },
|
||||
// }
|
||||
}
|
||||
|
||||
/// Create a new [`Attribute`]
|
||||
pub fn attr<'a>(
|
||||
&'a self,
|
||||
name: &'static str,
|
||||
val: impl IntoAttributeValue<'a>,
|
||||
value: impl IntoAttributeValue<'a>,
|
||||
namespace: Option<&'static str>,
|
||||
volatile: bool,
|
||||
) -> Attribute<'a> {
|
||||
@@ -65,7 +71,7 @@ impl ScopeState {
|
||||
name,
|
||||
namespace,
|
||||
volatile,
|
||||
value: val.into_value(self.bump()),
|
||||
value: value.into_value(self.bump()),
|
||||
mounted_element: Cell::new(ElementId(0)),
|
||||
}
|
||||
}
|
||||
@@ -97,6 +103,7 @@ impl ScopeState {
|
||||
static_props: P::IS_STATIC,
|
||||
props: Cell::new(detached_dyn),
|
||||
placeholder: Cell::new(None),
|
||||
scope: Cell::new(None),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -137,39 +144,100 @@ impl<'a> RenderReturn<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
pub trait IntoVnode<'a, A = ()> {
|
||||
fn into_dynamic_node(self, cx: &'a ScopeState) -> VNode<'a>;
|
||||
pub trait IntoVNode<'a, A = (), J = ()> {
|
||||
fn into_vnode(self, cx: &'a ScopeState) -> DynamicNode<'a>;
|
||||
}
|
||||
|
||||
impl<'a, 'b> IntoVnode<'a> for LazyNodes<'a, 'b> {
|
||||
fn into_dynamic_node(self, cx: &'a ScopeState) -> VNode<'a> {
|
||||
self.call(cx)
|
||||
impl<'a, 'b> IntoVNode<'a> for VNode<'a> {
|
||||
fn into_vnode(self, _cx: &'a ScopeState) -> DynamicNode<'a> {
|
||||
todo!()
|
||||
// self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> IntoVnode<'a> for VNode<'a> {
|
||||
fn into_dynamic_node(self, _cx: &'a ScopeState) -> VNode<'a> {
|
||||
self
|
||||
impl<'a, 'b> IntoVNode<'a> for LazyNodes<'a, 'b> {
|
||||
fn into_vnode(self, cx: &'a ScopeState) -> DynamicNode<'a> {
|
||||
todo!()
|
||||
// self.call(cx)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> IntoVnode<'a> for Option<VNode<'a>> {
|
||||
fn into_dynamic_node(self, _cx: &'a ScopeState) -> VNode<'a> {
|
||||
self.expect("please allow optional nodes in")
|
||||
impl<'b> IntoVNode<'_> for &'b str {
|
||||
fn into_vnode(self, cx: &ScopeState) -> DynamicNode {
|
||||
// cx.text(format_args!("{}", self))
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> IntoVnode<'a> for &'a VNode<'a> {
|
||||
fn into_dynamic_node(self, _cx: &'a ScopeState) -> VNode<'a> {
|
||||
VNode {
|
||||
node_id: self.node_id.clone(),
|
||||
parent: self.parent,
|
||||
template: self.template,
|
||||
root_ids: self.root_ids,
|
||||
key: self.key,
|
||||
dynamic_nodes: self.dynamic_nodes,
|
||||
dynamic_attrs: self.dynamic_attrs,
|
||||
impl IntoVNode<'_> for String {
|
||||
fn into_vnode(self, cx: &ScopeState) -> DynamicNode {
|
||||
// cx.text(format_args!("{}", self))
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'b> IntoVNode<'b> for Arguments<'_> {
|
||||
fn into_vnode(self, cx: &'b ScopeState) -> DynamicNode<'b> {
|
||||
// cx.text(self)
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> IntoVNode<'a> for &VNode<'a> {
|
||||
fn into_vnode(self, _cx: &'a ScopeState) -> DynamicNode<'a> {
|
||||
todo!()
|
||||
// VNode {
|
||||
// node_id: self.node_id.clone(),
|
||||
// parent: self.parent,
|
||||
// template: self.template,
|
||||
// root_ids: self.root_ids,
|
||||
// key: self.key,
|
||||
// dynamic_nodes: self.dynamic_nodes,
|
||||
// dynamic_attrs: self.dynamic_attrs,
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
// Note that we're using the E as a generic but this is never crafted anyways.
|
||||
pub struct FromNodeIterator;
|
||||
impl<'a, T, I, E> IntoVNode<'a, FromNodeIterator, E> for T
|
||||
where
|
||||
T: IntoIterator<Item = I>,
|
||||
I: IntoVNode<'a, E>,
|
||||
{
|
||||
fn into_vnode(self, cx: &'a ScopeState) -> DynamicNode<'a> {
|
||||
let mut nodes = bumpalo::collections::Vec::new_in(cx.bump());
|
||||
|
||||
for node in self {
|
||||
nodes.push(node.into_vnode(cx));
|
||||
}
|
||||
|
||||
let children = nodes.into_bump_slice();
|
||||
|
||||
// if cfg!(debug_assertions) && children.len() > 1 && children.last().unwrap().key().is_none()
|
||||
// {
|
||||
// let bt = backtrace::Backtrace::new();
|
||||
// let bt = "no backtrace available";
|
||||
|
||||
// // todo: make the backtrace prettier or remove it altogether
|
||||
// log::error!(
|
||||
// r#"
|
||||
// Warning: Each child in an array or iterator should have a unique "key" prop.
|
||||
// Not providing a key will lead to poor performance with lists.
|
||||
// See docs.rs/dioxus for more information.
|
||||
// -------------
|
||||
// {:?}
|
||||
// "#,
|
||||
// bt
|
||||
// );
|
||||
// }
|
||||
|
||||
todo!()
|
||||
// VNode::Fragment(cx.bump.alloc(VFragment {
|
||||
// children,
|
||||
// placeholder: Default::default(),
|
||||
// key: None,
|
||||
// }))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -25,8 +25,9 @@ impl<'b> VirtualDom {
|
||||
todo!()
|
||||
}
|
||||
|
||||
DynamicNode::Fragment(children) => {}
|
||||
DynamicNode::Fragment { inner, nodes } => {}
|
||||
DynamicNode::Placeholder(_) => todo!(),
|
||||
_ => todo!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,7 +92,6 @@ pub use crate::innerlude::{
|
||||
Template,
|
||||
TemplateAttribute,
|
||||
TemplateNode,
|
||||
UiEvent,
|
||||
VNode,
|
||||
VirtualDom,
|
||||
};
|
||||
@@ -102,9 +101,9 @@ pub use crate::innerlude::{
|
||||
/// This includes types like [`Scope`], [`Element`], and [`Component`].
|
||||
pub mod prelude {
|
||||
pub use crate::innerlude::{
|
||||
fc_to_builder, Element, EventPriority, Fragment, LazyNodes, NodeFactory, Properties, Scope,
|
||||
ScopeId, ScopeState, Scoped, TaskId, Template, TemplateAttribute, TemplateNode, UiEvent,
|
||||
VNode, VirtualDom,
|
||||
fc_to_builder, Element, EventHandler, EventPriority, Fragment, LazyNodes, NodeFactory,
|
||||
Properties, Scope, ScopeId, ScopeState, Scoped, TaskId, Template, TemplateAttribute,
|
||||
TemplateNode, VNode, VirtualDom,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -35,6 +35,7 @@ impl std::ops::DerefMut for Mutations<'_> {
|
||||
each subtree has its own numbering scheme
|
||||
*/
|
||||
|
||||
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub enum Mutation<'a> {
|
||||
SetAttribute {
|
||||
@@ -43,6 +44,12 @@ pub enum Mutation<'a> {
|
||||
id: ElementId,
|
||||
},
|
||||
|
||||
SetBoolAttribute {
|
||||
name: &'a str,
|
||||
value: bool,
|
||||
id: ElementId,
|
||||
},
|
||||
|
||||
LoadTemplate {
|
||||
name: &'static str,
|
||||
index: usize,
|
||||
@@ -86,6 +93,10 @@ pub enum Mutation<'a> {
|
||||
id: ElementId,
|
||||
},
|
||||
|
||||
SetInnerText {
|
||||
value: &'a str,
|
||||
},
|
||||
|
||||
CreateText {
|
||||
value: &'a str,
|
||||
},
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::{any_props::AnyProps, arena::ElementId, ScopeState};
|
||||
use crate::{any_props::AnyProps, arena::ElementId, ScopeId, ScopeState};
|
||||
use std::{
|
||||
any::{Any, TypeId},
|
||||
cell::{Cell, RefCell},
|
||||
@@ -15,7 +15,7 @@ pub struct VNode<'a> {
|
||||
pub key: Option<&'a str>,
|
||||
|
||||
// When rendered, this template will be linked to its parent manually
|
||||
pub parent: Option<(*mut VNode<'static>, usize)>,
|
||||
pub parent: Option<*const DynamicNode<'a>>,
|
||||
|
||||
pub template: Template<'static>,
|
||||
|
||||
@@ -98,6 +98,7 @@ pub enum TemplateNode<'a> {
|
||||
namespace: Option<&'a str>,
|
||||
attrs: &'a [TemplateAttribute<'a>],
|
||||
children: &'a [TemplateNode<'a>],
|
||||
inner_opt: bool,
|
||||
},
|
||||
Text(&'a str),
|
||||
Dynamic(usize),
|
||||
@@ -110,12 +111,17 @@ pub enum DynamicNode<'a> {
|
||||
static_props: bool,
|
||||
props: Cell<*mut dyn AnyProps<'a>>,
|
||||
placeholder: Cell<Option<ElementId>>,
|
||||
scope: Cell<Option<ScopeId>>,
|
||||
},
|
||||
Text {
|
||||
id: Cell<ElementId>,
|
||||
value: &'a str,
|
||||
inner: bool,
|
||||
},
|
||||
Fragment {
|
||||
nodes: &'a [VNode<'a>],
|
||||
inner: bool,
|
||||
},
|
||||
Fragment(&'a [VNode<'a>]),
|
||||
Placeholder(Cell<ElementId>),
|
||||
}
|
||||
|
||||
@@ -215,3 +221,54 @@ fn what_are_the_sizes() {
|
||||
dbg!(std::mem::size_of::<Template>());
|
||||
dbg!(std::mem::size_of::<TemplateNode>());
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
|
||||
SSR includes data-id which allows O(1) hydration
|
||||
|
||||
|
||||
we read the edit stream dn then we can just rehydare
|
||||
|
||||
|
||||
|
||||
ideas:
|
||||
- IDs for lookup
|
||||
- use edit stream to hydrate
|
||||
- write comments to dom that specify size of children
|
||||
|
||||
IDs for lookups
|
||||
- adds noise to generated html
|
||||
- doesnt work for text nodes
|
||||
- suspense could cause ordering to be weird
|
||||
|
||||
Names for lookups:
|
||||
- label each root or something with the template name
|
||||
- label each dynamic node with a path
|
||||
- noisy too
|
||||
- allows reverse lookups
|
||||
|
||||
Ideal:
|
||||
- no noise in the dom
|
||||
- fast, ideally O(1)
|
||||
- able to pick apart text nodes that get merged during SSR
|
||||
|
||||
|
||||
--> render vdom
|
||||
--> traverse vdom and real dom simultaneously
|
||||
|
||||
IE
|
||||
|
||||
div {
|
||||
div {
|
||||
div {
|
||||
"thing"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
*/
|
||||
@@ -17,7 +17,7 @@ use std::{
|
||||
};
|
||||
|
||||
impl VirtualDom {
|
||||
pub fn new_scope(&mut self, props: *mut dyn AnyProps<'static>) -> &mut ScopeState {
|
||||
pub(super) fn new_scope(&mut self, props: *mut dyn AnyProps<'static>) -> &mut ScopeState {
|
||||
let parent = self.acquire_current_scope_raw();
|
||||
let container = self.acquire_current_container();
|
||||
let entry = self.scopes.vacant_entry();
|
||||
|
||||
@@ -4,18 +4,21 @@ use crate::{
|
||||
arena::ElementPath,
|
||||
diff::DirtyScope,
|
||||
factory::RenderReturn,
|
||||
innerlude::{is_path_ascendant, Mutations, Scheduler, SchedulerMsg},
|
||||
innerlude::{Mutations, Scheduler, SchedulerMsg},
|
||||
mutations::Mutation,
|
||||
nodes::{Template, TemplateId},
|
||||
scheduler::{SuspenseBoundary, SuspenseId},
|
||||
scopes::{ScopeId, ScopeState},
|
||||
Attribute, AttributeValue, Element, Scope, SuspenseContext, UiEvent,
|
||||
Attribute, AttributeValue, Element, EventPriority, Scope, SuspenseContext,
|
||||
};
|
||||
use futures_util::{pin_mut, FutureExt, StreamExt};
|
||||
use slab::Slab;
|
||||
use std::collections::{BTreeSet, HashMap};
|
||||
use std::future::Future;
|
||||
use std::rc::Rc;
|
||||
use std::{
|
||||
any::Any,
|
||||
collections::{BTreeSet, HashMap},
|
||||
};
|
||||
|
||||
/// A virtual node system that progresses user events and diffs UI trees.
|
||||
///
|
||||
@@ -275,42 +278,68 @@ impl VirtualDom {
|
||||
!self.scheduler.leaves.borrow().is_empty()
|
||||
}
|
||||
|
||||
/// Returns None if no element could be found
|
||||
pub fn handle_event<T: 'static>(&mut self, event: &UiEvent<T>) -> Option<()> {
|
||||
let path = self.elements.get(event.element.0)?;
|
||||
/// Call a listener inside the VirtualDom with data from outside the VirtualDom.
|
||||
///
|
||||
/// This method will identify the appropriate element
|
||||
///
|
||||
///
|
||||
///
|
||||
///
|
||||
///
|
||||
///
|
||||
///
|
||||
pub fn handle_event(
|
||||
&mut self,
|
||||
name: &str,
|
||||
event: &dyn Any,
|
||||
element: ElementId,
|
||||
bubbles: bool,
|
||||
priority: EventPriority,
|
||||
) -> Option<()> {
|
||||
/*
|
||||
- click registers
|
||||
- walk upwards until first element with onclick listener
|
||||
- get template from ElementID
|
||||
- break out of wait loop
|
||||
- send event to virtualdom
|
||||
*/
|
||||
|
||||
let location = unsafe { &mut *path.template }
|
||||
let path = &self.elements[element.0];
|
||||
let template = unsafe { &*path.template };
|
||||
let dynamic = &template.dynamic_nodes[path.element];
|
||||
|
||||
let location = template
|
||||
.dynamic_attrs
|
||||
.iter()
|
||||
.position(|attr| attr.mounted_element.get() == event.element)?;
|
||||
.position(|attr| attr.mounted_element.get() == element)?;
|
||||
|
||||
let mut index = Some((path.template, location));
|
||||
// let mut index = Some((path.template, location));
|
||||
|
||||
let mut listeners = Vec::<&Attribute>::new();
|
||||
|
||||
while let Some((raw_parent, dyn_index)) = index {
|
||||
let parent = unsafe { &mut *raw_parent };
|
||||
let path = parent.template.node_paths[dyn_index];
|
||||
// while let Some((raw_parent, dyn_index)) = index {
|
||||
// let parent = unsafe { &mut *raw_parent };
|
||||
// let path = parent.template.node_paths[dyn_index];
|
||||
|
||||
listeners.extend(
|
||||
parent
|
||||
.dynamic_attrs
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter_map(|(idx, attr)| {
|
||||
match is_path_ascendant(parent.template.node_paths[idx], path) {
|
||||
true if attr.name == event.name => Some(attr),
|
||||
_ => None,
|
||||
}
|
||||
}),
|
||||
);
|
||||
// listeners.extend(
|
||||
// parent
|
||||
// .dynamic_attrs
|
||||
// .iter()
|
||||
// .enumerate()
|
||||
// .filter_map(|(idx, attr)| {
|
||||
// match is_path_ascendant(parent.template.node_paths[idx], path) {
|
||||
// true if attr.name == event.name => Some(attr),
|
||||
// _ => None,
|
||||
// }
|
||||
// }),
|
||||
// );
|
||||
|
||||
index = parent.parent;
|
||||
}
|
||||
// index = parent.parent;
|
||||
// }
|
||||
|
||||
for listener in listeners {
|
||||
if let AttributeValue::Listener(listener) = &listener.value {
|
||||
(listener.borrow_mut())(&event.event)
|
||||
(listener.borrow_mut())(event)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user