Feat: begin WIP on html macro
This commit is contained in:
1 parent
b0a87e5d73
commit
a8b1225c48
11 files changed
+255
-102
No files matched your search
@@ -19,11 +19,9 @@
|
|||||||
//!
|
//!
|
||||||
//!
|
//!
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
use bumpalo::Bump;
|
use bumpalo::Bump;
|
||||||
|
|
||||||
use crate::innerlude::{Listener};
|
use crate::innerlude::Listener;
|
||||||
|
|
||||||
/// The `Edit` represents a single modifcation of the renderer tree.
|
/// The `Edit` represents a single modifcation of the renderer tree.
|
||||||
///
|
///
|
||||||
@@ -308,14 +306,19 @@ impl<'a> EditMachine<'a> {
|
|||||||
self.forcing_new_listeners = previous;
|
self.forcing_new_listeners = previous;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_event_listener(&mut self, _listener: &Listener) {
|
pub fn new_event_listener(&mut self, listener: &Listener) {
|
||||||
debug_assert!(self.traversal_is_committed());
|
debug_assert!(self.traversal_is_committed());
|
||||||
todo!("Event listener not wired up yet");
|
// todo!("Event listener not wired up yet");
|
||||||
// debug!("emit: new_event_listener({:?})", listener);
|
log::debug!("emit: new_event_listener({:?})", listener);
|
||||||
// let (a, b) = listener.get_callback_parts();
|
let (a, b) = listener.get_callback_parts();
|
||||||
// debug_assert!(a != 0);
|
debug_assert!(a != 0);
|
||||||
// let event_id = self.ensure_string(listener.event);
|
// let event_id = self.ensure_string(listener.event);
|
||||||
// self.emitter.new_event_listener(event_id.into(), a, b);
|
self.emitter.push(Edit::NewEventListener {
|
||||||
|
event_type: listener.event.into(),
|
||||||
|
a,
|
||||||
|
b,
|
||||||
|
});
|
||||||
|
// self.emitter.new_event_listener(listener.event.into(), a, b);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_event_listener(&mut self, listener: &Listener) {
|
pub fn update_event_listener(&mut self, listener: &Listener) {
|
||||||
@@ -326,19 +329,25 @@ impl<'a> EditMachine<'a> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// debug!("emit: update_event_listener({:?})", listener);
|
log::debug!("emit: update_event_listener({:?})", listener);
|
||||||
todo!("Event listener not wired up yet");
|
// todo!("Event listener not wired up yet");
|
||||||
// let (a, b) = listener.get_callback_parts();
|
let (a, b) = listener.get_callback_parts();
|
||||||
// debug_assert!(a != 0);
|
debug_assert!(a != 0);
|
||||||
let _event_id = self.ensure_string(listener.event);
|
self.emitter.push(Edit::UpdateEventListener {
|
||||||
|
event_type: listener.event.into(),
|
||||||
|
a,
|
||||||
|
b,
|
||||||
|
});
|
||||||
// self.emitter.update_event_listener(event_id.into(), a, b);
|
// self.emitter.update_event_listener(event_id.into(), a, b);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn remove_event_listener(&mut self, event: &str) {
|
pub fn remove_event_listener(&mut self, event: &'a str) {
|
||||||
debug_assert!(self.traversal_is_committed());
|
debug_assert!(self.traversal_is_committed());
|
||||||
|
self.emitter
|
||||||
|
.push(Edit::RemoveEventListener { event_type: event });
|
||||||
// debug!("emit: remove_event_listener({:?})", event);
|
// debug!("emit: remove_event_listener({:?})", event);
|
||||||
let _event_id = self.ensure_string(event);
|
// let _event_id = self.ensure_string(event);
|
||||||
todo!("Event listener not wired up yet");
|
// todo!("Event listener not wired up yet");
|
||||||
// self.emitter.remove_event_listener(event_id.into());
|
// self.emitter.remove_event_listener(event_id.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -63,13 +63,9 @@ pub struct DiffMachine<'a> {
|
|||||||
need_to_diff: FxHashSet<Index>,
|
need_to_diff: FxHashSet<Index>,
|
||||||
}
|
}
|
||||||
|
|
||||||
enum NeedToDiff {
|
|
||||||
PropsChanged,
|
|
||||||
Subscription,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> DiffMachine<'a> {
|
impl<'a> DiffMachine<'a> {
|
||||||
pub fn new(bump: &'a Bump) -> Self {
|
pub fn new(bump: &'a Bump) -> Self {
|
||||||
|
log::debug!("starsting diff machine");
|
||||||
Self {
|
Self {
|
||||||
change_list: EditMachine::new(bump),
|
change_list: EditMachine::new(bump),
|
||||||
immediate_queue: Vec::new(),
|
immediate_queue: Vec::new(),
|
||||||
@@ -83,6 +79,7 @@ impl<'a> DiffMachine<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn diff_node(&mut self, old: &VNode<'a>, new: &VNode<'a>) {
|
pub fn diff_node(&mut self, old: &VNode<'a>, new: &VNode<'a>) {
|
||||||
|
log::debug!("Diffing nodes");
|
||||||
/*
|
/*
|
||||||
For each valid case, we "commit traversal", meaning we save this current position in the tree.
|
For each valid case, we "commit traversal", meaning we save this current position in the tree.
|
||||||
Then, we diff and queue an edit event (via chagelist). s single trees - when components show up, we save that traversal and then re-enter later.
|
Then, we diff and queue an edit event (via chagelist). s single trees - when components show up, we save that traversal and then re-enter later.
|
||||||
@@ -876,6 +873,7 @@ impl<'a> DiffMachine<'a> {
|
|||||||
children,
|
children,
|
||||||
namespace,
|
namespace,
|
||||||
}) => {
|
}) => {
|
||||||
|
log::info!("Creating {:#?}", node);
|
||||||
if let Some(namespace) = namespace {
|
if let Some(namespace) = namespace {
|
||||||
self.change_list.create_element_ns(tag_name, namespace);
|
self.change_list.create_element_ns(tag_name, namespace);
|
||||||
} else {
|
} else {
|
||||||
@@ -1002,7 +1000,6 @@ enum KeyedPrefixResult {
|
|||||||
|
|
||||||
mod support {
|
mod support {
|
||||||
|
|
||||||
|
|
||||||
// // Get or create the template.
|
// // Get or create the template.
|
||||||
// //
|
// //
|
||||||
// // Upon entering this function the change list stack may be in any shape:
|
// // Upon entering this function the change list stack may be in any shape:
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
//! The goal here is to provide a consistent event interface across all renderer types
|
//! The goal here is to provide a consistent event interface across all renderer types
|
||||||
use generational_arena::Index;
|
use generational_arena::Index;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct EventTrigger {
|
pub struct EventTrigger {
|
||||||
pub component_id: Index,
|
pub component_id: Index,
|
||||||
pub listener_id: u32,
|
pub listener_id: u32,
|
||||||
@@ -18,6 +19,7 @@ impl EventTrigger {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub enum VirtualEvent {
|
pub enum VirtualEvent {
|
||||||
// Real events
|
// Real events
|
||||||
ClipboardEvent(ClipboardEvent),
|
ClipboardEvent(ClipboardEvent),
|
||||||
@@ -42,19 +44,35 @@ pub enum VirtualEvent {
|
|||||||
|
|
||||||
// these should reference the underlying event
|
// these should reference the underlying event
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct ClipboardEvent {}
|
pub struct ClipboardEvent {}
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct CompositionEvent {}
|
pub struct CompositionEvent {}
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct KeyboardEvent {}
|
pub struct KeyboardEvent {}
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct FocusEvent {}
|
pub struct FocusEvent {}
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct FormEvent {}
|
pub struct FormEvent {}
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct GenericEvent {}
|
pub struct GenericEvent {}
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct MouseEvent {}
|
pub struct MouseEvent {}
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct PointerEvent {}
|
pub struct PointerEvent {}
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct SelectionEvent {}
|
pub struct SelectionEvent {}
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct TouchEvent {}
|
pub struct TouchEvent {}
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct UIEvent {}
|
pub struct UIEvent {}
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct WheelEvent {}
|
pub struct WheelEvent {}
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct MediaEvent {}
|
pub struct MediaEvent {}
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct ImageEvent {}
|
pub struct ImageEvent {}
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct AnimationEvent {}
|
pub struct AnimationEvent {}
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct TransitionEvent {}
|
pub struct TransitionEvent {}
|
||||||
@@ -1070,7 +1070,8 @@ pub fn attr<'a>(name: &'static str, value: &'a str) -> Attribute<'a> {
|
|||||||
/// // do something when a click happens...
|
/// // do something when a click happens...
|
||||||
/// });
|
/// });
|
||||||
/// ```
|
/// ```
|
||||||
pub fn on<'a, 'b, F: 'static>(
|
pub fn on<'a, 'b>(
|
||||||
|
// pub fn on<'a, 'b, F: 'static>(
|
||||||
bump: &'a Bump,
|
bump: &'a Bump,
|
||||||
event: &'static str,
|
event: &'static str,
|
||||||
callback: impl Fn(()) + 'a,
|
callback: impl Fn(()) + 'a,
|
||||||
|
|||||||
@@ -3,8 +3,6 @@
|
|||||||
//!
|
//!
|
||||||
//! These VNodes should be *very* cheap and *very* fast to construct - building a full tree should be insanely quick.
|
//! These VNodes should be *very* cheap and *very* fast to construct - building a full tree should be insanely quick.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
use bumpalo::Bump;
|
use bumpalo::Bump;
|
||||||
pub use vcomponent::VComponent;
|
pub use vcomponent::VComponent;
|
||||||
pub use velement::VElement;
|
pub use velement::VElement;
|
||||||
@@ -97,7 +95,7 @@ mod vnode {
|
|||||||
|
|
||||||
mod velement {
|
mod velement {
|
||||||
use super::*;
|
use super::*;
|
||||||
use std::{fmt::Debug};
|
use std::fmt::Debug;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct VElement<'a> {
|
pub struct VElement<'a> {
|
||||||
@@ -199,6 +197,29 @@ mod velement {
|
|||||||
.finish()
|
.finish()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
pub(crate) type ListenerCallback<'a> = &'a (dyn Fn(()));
|
||||||
|
union CallbackFatPtr<'a> {
|
||||||
|
callback: ListenerCallback<'a>,
|
||||||
|
parts: (u32, u32),
|
||||||
|
}
|
||||||
|
impl Listener<'_> {
|
||||||
|
#[inline]
|
||||||
|
pub(crate) fn get_callback_parts(&self) -> (u32, u32) {
|
||||||
|
assert_eq!(
|
||||||
|
std::mem::size_of::<ListenerCallback>(),
|
||||||
|
std::mem::size_of::<CallbackFatPtr>()
|
||||||
|
);
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
let fat = CallbackFatPtr {
|
||||||
|
callback: self.callback,
|
||||||
|
};
|
||||||
|
let (a, b) = fat.parts;
|
||||||
|
debug_assert!(a != 0);
|
||||||
|
(a, b)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// The key for keyed children.
|
/// The key for keyed children.
|
||||||
///
|
///
|
||||||
@@ -265,9 +286,7 @@ mod vtext {
|
|||||||
/// Only supports the functional syntax
|
/// Only supports the functional syntax
|
||||||
mod vcomponent {
|
mod vcomponent {
|
||||||
use crate::innerlude::FC;
|
use crate::innerlude::FC;
|
||||||
use std::{marker::PhantomData};
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct VComponent<'src> {
|
pub struct VComponent<'src> {
|
||||||
|
|||||||
@@ -219,10 +219,29 @@ impl ActiveFrame {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
// #[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
static ListenerTest: FC<()> = |ctx, props| {
|
||||||
|
ctx.view(html! {
|
||||||
|
<div onclick={|_| println!("Hell owlrld")}>
|
||||||
|
"hello"
|
||||||
|
</div>
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn check_listeners() -> Result<()> {
|
||||||
|
let mut scope = Scope::new::<(), ()>(ListenerTest, (), None);
|
||||||
|
scope.run::<()>();
|
||||||
|
|
||||||
|
let nodes = scope.new_frame();
|
||||||
|
dbg!(nodes);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_scope() {
|
fn test_scope() {
|
||||||
let example: FC<()> = |ctx, props| {
|
let example: FC<()> = |ctx, props| {
|
||||||
|
|||||||
@@ -1,3 +1,18 @@
|
|||||||
|
//!
|
||||||
|
//! TODO:
|
||||||
|
//! - [ ] Support for VComponents
|
||||||
|
//! - [ ] Support for inline format in text
|
||||||
|
//! - [ ] Support for expressions in attribute positions
|
||||||
|
//! - [ ] Support for iterators
|
||||||
|
//!
|
||||||
|
//!
|
||||||
|
//!
|
||||||
|
//!
|
||||||
|
//!
|
||||||
|
//!
|
||||||
|
//!
|
||||||
|
//!
|
||||||
|
|
||||||
use ::{
|
use ::{
|
||||||
proc_macro::TokenStream,
|
proc_macro::TokenStream,
|
||||||
proc_macro2::{Span, TokenStream as TokenStream2},
|
proc_macro2::{Span, TokenStream as TokenStream2},
|
||||||
@@ -10,6 +25,8 @@ use ::{
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// The html! macro makes it easy for developers to write jsx-style markup in their components.
|
||||||
|
/// We aim to keep functional parity with html templates.
|
||||||
#[proc_macro]
|
#[proc_macro]
|
||||||
pub fn html(s: TokenStream) -> TokenStream {
|
pub fn html(s: TokenStream) -> TokenStream {
|
||||||
let html: HtmlRender = match syn::parse(s) {
|
let html: HtmlRender = match syn::parse(s) {
|
||||||
@@ -19,8 +36,10 @@ pub fn html(s: TokenStream) -> TokenStream {
|
|||||||
html.to_token_stream().into()
|
html.to_token_stream().into()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ==============================================
|
||||||
|
// Parse any stream coming from the html! macro
|
||||||
|
// ==============================================
|
||||||
struct HtmlRender {
|
struct HtmlRender {
|
||||||
// ctx: Ident,
|
|
||||||
kind: NodeOrList,
|
kind: NodeOrList,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -58,6 +77,9 @@ impl ToTokens for HtmlRender {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// =============================================
|
||||||
|
/// Parse any child as a node or list of nodes
|
||||||
|
/// =============================================
|
||||||
enum NodeOrList {
|
enum NodeOrList {
|
||||||
Node(Node),
|
Node(Node),
|
||||||
List(NodeList),
|
List(NodeList),
|
||||||
@@ -115,6 +137,9 @@ impl Parse for Node {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// =======================================
|
||||||
|
/// Parse the VNode::Element type
|
||||||
|
/// =======================================
|
||||||
struct Element {
|
struct Element {
|
||||||
name: Ident,
|
name: Ident,
|
||||||
attrs: Vec<Attr>,
|
attrs: Vec<Attr>,
|
||||||
@@ -214,6 +239,9 @@ impl Parse for Element {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// =======================================
|
||||||
|
/// Parse a VElement's Attributes
|
||||||
|
/// =======================================
|
||||||
struct Attr {
|
struct Attr {
|
||||||
name: Ident,
|
name: Ident,
|
||||||
ty: AttrType,
|
ty: AttrType,
|
||||||
@@ -285,6 +313,10 @@ enum AttrType {
|
|||||||
// todo Bool(MaybeExpr<LitBool>)
|
// todo Bool(MaybeExpr<LitBool>)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// =======================================
|
||||||
|
/// Parse just plain text
|
||||||
|
/// =======================================
|
||||||
|
///
|
||||||
struct TextNode(MaybeExpr<LitStr>);
|
struct TextNode(MaybeExpr<LitStr>);
|
||||||
|
|
||||||
impl Parse for TextNode {
|
impl Parse for TextNode {
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ features = [
|
|||||||
"Document",
|
"Document",
|
||||||
"Element",
|
"Element",
|
||||||
"HtmlElement",
|
"HtmlElement",
|
||||||
|
"HtmlInputElement",
|
||||||
"EventTarget",
|
"EventTarget",
|
||||||
"HtmlCollection",
|
"HtmlCollection",
|
||||||
"Node",
|
"Node",
|
||||||
@@ -39,6 +40,7 @@ features = [
|
|||||||
"InputEvent",
|
"InputEvent",
|
||||||
"DocumentType",
|
"DocumentType",
|
||||||
"CharacterData",
|
"CharacterData",
|
||||||
|
"HtmlOptionElement",
|
||||||
]
|
]
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
|
|||||||
@@ -4,17 +4,38 @@ use dioxus_core::prelude::*;
|
|||||||
use dioxus_web::*;
|
use dioxus_web::*;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
// Setup logging
|
||||||
|
wasm_logger::init(wasm_logger::Config::new(log::Level::Debug));
|
||||||
|
// wasm_logger::init(wasm_logger::Config::with_prefix(
|
||||||
|
// log::Level::Debug,
|
||||||
|
// "dioxus_core",
|
||||||
|
// ));
|
||||||
|
console_error_panic_hook::set_once();
|
||||||
|
|
||||||
|
// Run the app
|
||||||
wasm_bindgen_futures::spawn_local(WebsysRenderer::start(App));
|
wasm_bindgen_futures::spawn_local(WebsysRenderer::start(App));
|
||||||
}
|
}
|
||||||
|
|
||||||
static App: FC<()> = |ctx, _| {
|
static App: FC<()> = |ctx, _| {
|
||||||
ctx.view(html! {
|
log::info!("Ran component");
|
||||||
<div>
|
use dioxus::builder::*;
|
||||||
<div class="flex items-center justify-center flex-col">
|
ctx.view(|b| {
|
||||||
<div class="font-bold text-xl"> "Count is {}" </div>
|
div(b)
|
||||||
<button onclick={move |_| log::info!("button1 clicked!")}> "increment" </button>
|
.child(text("hello"))
|
||||||
<button onclick={move |_| log::info!("button2 clicked!")}> "decrement" </button>
|
.listeners([on(b, "click", |_| {
|
||||||
</div>
|
//
|
||||||
</div>
|
log::info!("button1 clicked!");
|
||||||
|
})])
|
||||||
|
.finish()
|
||||||
})
|
})
|
||||||
|
// ctx.view(html! {
|
||||||
|
// <div onclick={move |_| log::info!("button1 clicked!")}>
|
||||||
|
// "Hello"
|
||||||
|
// // <div class="flex items-center justify-center flex-col">
|
||||||
|
// // <div class="font-bold text-xl"> "Count is ..." </div>
|
||||||
|
// // <button onclick={move |_| log::info!("button1 clicked!")}> "increment" </button>
|
||||||
|
// // <button onclick={move |_| log::info!("button2 clicked!")}> "decrement" </button>
|
||||||
|
// // </div>
|
||||||
|
// </div>
|
||||||
|
// })
|
||||||
};
|
};
|
||||||
@@ -1,22 +1,42 @@
|
|||||||
use dioxus_core::changelist::Edit;
|
use std::fmt::Debug;
|
||||||
|
|
||||||
|
use dioxus_core::{changelist::Edit, events::EventTrigger};
|
||||||
use fxhash::FxHashMap;
|
use fxhash::FxHashMap;
|
||||||
use log::debug;
|
use log::debug;
|
||||||
use wasm_bindgen::{closure::Closure, JsCast};
|
use wasm_bindgen::{closure::Closure, JsCast};
|
||||||
use web_sys::{window, Document, Element, Event, Node};
|
use web_sys::{window, Document, Element, Event, HtmlInputElement, HtmlOptionElement, Node};
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
|
#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
|
||||||
pub struct CacheId(u32);
|
pub struct CacheId(u32);
|
||||||
|
|
||||||
|
struct RootCallback(Box<dyn Fn(EventTrigger)>);
|
||||||
|
impl std::fmt::Debug for RootCallback {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(crate) struct PatchMachine {
|
pub(crate) struct PatchMachine {
|
||||||
container: Element,
|
|
||||||
pub(crate) stack: Stack,
|
pub(crate) stack: Stack,
|
||||||
|
|
||||||
|
root: Element,
|
||||||
|
|
||||||
temporaries: FxHashMap<u32, Node>,
|
temporaries: FxHashMap<u32, Node>,
|
||||||
callback: Option<Closure<dyn FnMut(&Event)>>,
|
|
||||||
|
// callback: RootCallback,
|
||||||
|
// callback: Option<Closure<dyn Fn(EventTrigger)>>,
|
||||||
document: Document,
|
document: Document,
|
||||||
// templates: FxHashMap<CacheId, Node>,
|
|
||||||
|
// every callback gets a monotomically increasing callback ID
|
||||||
|
callback_id: usize,
|
||||||
|
|
||||||
|
// Map of callback_id to component index and listener id
|
||||||
|
callback_map: FxHashMap<usize, (usize, usize)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// templates: FxHashMap<CacheId, Node>,
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
pub struct Stack {
|
pub struct Stack {
|
||||||
list: Vec<Node>,
|
list: Vec<Node>,
|
||||||
@@ -30,13 +50,13 @@ impl Stack {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn push(&mut self, node: Node) {
|
pub fn push(&mut self, node: Node) {
|
||||||
debug!("stack-push: {:?}", node);
|
// debug!("stack-push: {:?}", node);
|
||||||
self.list.push(node);
|
self.list.push(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn pop(&mut self) -> Node {
|
pub fn pop(&mut self) -> Node {
|
||||||
let res = self.list.pop().unwrap();
|
let res = self.list.pop().unwrap();
|
||||||
debug!("stack-pop: {:?}", res);
|
// debug!("stack-pop: {:?}", res);
|
||||||
|
|
||||||
res
|
res
|
||||||
}
|
}
|
||||||
@@ -46,10 +66,10 @@ impl Stack {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn top(&self) -> &Node {
|
pub fn top(&self) -> &Node {
|
||||||
log::info!(
|
// log::info!(
|
||||||
"Called top of stack with {} items remaining",
|
// "Called top of stack with {} items remaining",
|
||||||
self.list.len()
|
// self.list.len()
|
||||||
);
|
// );
|
||||||
match self.list.last() {
|
match self.list.last() {
|
||||||
Some(a) => a,
|
Some(a) => a,
|
||||||
None => panic!("Called 'top' of an empty stack, make sure to push the root first"),
|
None => panic!("Called 'top' of an empty stack, make sure to push the root first"),
|
||||||
@@ -58,19 +78,23 @@ impl Stack {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl PatchMachine {
|
impl PatchMachine {
|
||||||
pub fn new(container: Element) -> Self {
|
pub fn new(root: Element, event_callback: impl Fn(EventTrigger)) -> Self {
|
||||||
let document = window()
|
let document = window()
|
||||||
.expect("must have access to the window")
|
.expect("must have access to the window")
|
||||||
.document()
|
.document()
|
||||||
.expect("must have access to the Document");
|
.expect("must have access to the Document");
|
||||||
|
|
||||||
|
// attach all listeners to the container element
|
||||||
|
|
||||||
|
// templates: Default::default(),
|
||||||
Self {
|
Self {
|
||||||
// templates: Default::default(),
|
root,
|
||||||
container,
|
|
||||||
stack: Stack::with_capacity(20),
|
stack: Stack::with_capacity(20),
|
||||||
temporaries: Default::default(),
|
temporaries: Default::default(),
|
||||||
callback: None,
|
// callback: None,
|
||||||
document,
|
document,
|
||||||
|
callback_id: 0,
|
||||||
|
callback_map: FxHashMap::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -81,7 +105,7 @@ impl PatchMachine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn start(&mut self) {
|
pub fn start(&mut self) {
|
||||||
if let Some(child) = self.container.first_child() {
|
if let Some(child) = self.root.first_child() {
|
||||||
self.stack.push(child);
|
self.stack.push(child);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -96,9 +120,9 @@ impl PatchMachine {
|
|||||||
// self.templates.get(&id)
|
// self.templates.get(&id)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn init_events_trampoline(&mut self, _trampoline: ()) {
|
/// On any listener wakeup, find the listener that generated the event from th e attribute
|
||||||
todo!("Event trampoline not a thing anymore")
|
// pub fn init_events_trampoline(&mut self, _trampoline: ()) {
|
||||||
// pub fn init_events_trampoline(&mut self, mut trampoline: EventsTrampoline) {
|
pub fn init_events_trampoline(&mut self, event_channel: impl Fn(EventTrigger)) {
|
||||||
// self.callback = Some(Closure::wrap(Box::new(move |event: &web_sys::Event| {
|
// self.callback = Some(Closure::wrap(Box::new(move |event: &web_sys::Event| {
|
||||||
// let target = event
|
// let target = event
|
||||||
// .target()
|
// .target()
|
||||||
@@ -107,12 +131,12 @@ impl PatchMachine {
|
|||||||
// .expect("not a valid element");
|
// .expect("not a valid element");
|
||||||
// let typ = event.type_();
|
// let typ = event.type_();
|
||||||
// let a: u32 = target
|
// let a: u32 = target
|
||||||
// .get_attribute(&format!("dodrio-a-{}", typ))
|
// .get_attribute(&format!("dioxus-a-{}", typ))
|
||||||
// .and_then(|v| v.parse().ok())
|
// .and_then(|v| v.parse().ok())
|
||||||
// .unwrap_or_default();
|
// .unwrap_or_default();
|
||||||
|
|
||||||
// let b: u32 = target
|
// let b: u32 = target
|
||||||
// .get_attribute(&format!("dodrio-b-{}", typ))
|
// .get_attribute(&format!("dioxus-b-{}", typ))
|
||||||
// .and_then(|v| v.parse().ok())
|
// .and_then(|v| v.parse().ok())
|
||||||
// .unwrap_or_default();
|
// .unwrap_or_default();
|
||||||
|
|
||||||
@@ -180,40 +204,44 @@ impl PatchMachine {
|
|||||||
Edit::SetAttribute { name, value } => {
|
Edit::SetAttribute { name, value } => {
|
||||||
let node = self.stack.top();
|
let node = self.stack.top();
|
||||||
|
|
||||||
if let Some(node) = node.dyn_ref::<Element>() {
|
if let Some(node) = node.dyn_ref::<HtmlInputElement>() {
|
||||||
node.set_attribute(name, value).unwrap();
|
node.set_attribute(name, value).unwrap();
|
||||||
|
|
||||||
// Some attributes are "volatile" and don't work through `setAttribute`.
|
// Some attributes are "volatile" and don't work through `setAttribute`.
|
||||||
// TODO:
|
if name == "value" {
|
||||||
// if name == "value" {
|
node.set_value(value);
|
||||||
// node.set_value(value);
|
}
|
||||||
// }
|
if name == "checked" {
|
||||||
// if name == "checked" {
|
node.set_checked(true);
|
||||||
// node.set_checked(true);
|
}
|
||||||
// }
|
}
|
||||||
// if name == "selected" {
|
|
||||||
// node.set_selected(true);
|
if let Some(node) = node.dyn_ref::<HtmlOptionElement>() {
|
||||||
// }
|
if name == "selected" {
|
||||||
|
node.set_selected(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4
|
// 4
|
||||||
Edit::RemoveAttribute { name } => {
|
Edit::RemoveAttribute { name } => {
|
||||||
let node = self.stack.top();
|
let node = self.stack.top();
|
||||||
if let Some(node) = node.dyn_ref::<Element>() {
|
if let Some(node) = node.dyn_ref::<HtmlInputElement>() {
|
||||||
node.remove_attribute(name).unwrap();
|
node.remove_attribute(name).unwrap();
|
||||||
|
|
||||||
// Some attributes are "volatile" and don't work through `removeAttribute`.
|
// Some attributes are "volatile" and don't work through `removeAttribute`.
|
||||||
// TODO:
|
if name == "value" {
|
||||||
// if name == "value" {
|
node.set_value("");
|
||||||
// node.set_value("");
|
}
|
||||||
// }
|
if name == "checked" {
|
||||||
// if name == "checked" {
|
node.set_checked(false);
|
||||||
// node.set_checked(false);
|
}
|
||||||
// }
|
}
|
||||||
// if name == "selected" {
|
|
||||||
// node.set_selected(false);
|
if let Some(node) = node.dyn_ref::<HtmlOptionElement>() {
|
||||||
// }
|
if name == "selected" {
|
||||||
|
node.set_selected(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -266,29 +294,36 @@ impl PatchMachine {
|
|||||||
|
|
||||||
// 11
|
// 11
|
||||||
Edit::NewEventListener { event_type, a, b } => {
|
Edit::NewEventListener { event_type, a, b } => {
|
||||||
|
// attach the correct attributes to the element
|
||||||
|
// these will be used by accessing the event's target
|
||||||
|
// This ensures we only ever have one handler attached to the root, but decide
|
||||||
|
// dynamically when we want to call a listener.
|
||||||
|
|
||||||
let el = self.stack.top();
|
let el = self.stack.top();
|
||||||
|
|
||||||
let el = el
|
let el = el
|
||||||
.dyn_ref::<Element>()
|
.dyn_ref::<Element>()
|
||||||
.expect(&format!("not an element: {:?}", el));
|
.expect(&format!("not an element: {:?}", el));
|
||||||
el.add_event_listener_with_callback(
|
// el.add_event_listener_with_callback(
|
||||||
event_type,
|
// event_type,
|
||||||
self.callback.as_ref().unwrap().as_ref().unchecked_ref(),
|
// self.callback.as_ref().unwrap().as_ref().unchecked_ref(),
|
||||||
)
|
// )
|
||||||
.unwrap();
|
// .unwrap();
|
||||||
debug!("adding attributes: {}, {}", a, b);
|
debug!("adding attributes: {}, {}", a, b);
|
||||||
el.set_attribute(&format!("dodrio-a-{}", event_type), &a.to_string())
|
el.set_attribute(&format!("dioxus-a-{}", event_type), &a.to_string())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
el.set_attribute(&format!("dodrio-b-{}", event_type), &b.to_string())
|
el.set_attribute(&format!("dioxus-b-{}", event_type), &b.to_string())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 12
|
// 12
|
||||||
Edit::UpdateEventListener { event_type, a, b } => {
|
Edit::UpdateEventListener { event_type, a, b } => {
|
||||||
|
// update our internal mapping, and then modify the attribute
|
||||||
|
|
||||||
if let Some(el) = self.stack.top().dyn_ref::<Element>() {
|
if let Some(el) = self.stack.top().dyn_ref::<Element>() {
|
||||||
el.set_attribute(&format!("dodrio-a-{}", event_type), &a.to_string())
|
el.set_attribute(&format!("dioxus-a-{}", event_type), &a.to_string())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
el.set_attribute(&format!("dodrio-b-{}", event_type), &b.to_string())
|
el.set_attribute(&format!("dioxus-b-{}", event_type), &b.to_string())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -296,11 +331,11 @@ impl PatchMachine {
|
|||||||
// 13
|
// 13
|
||||||
Edit::RemoveEventListener { event_type } => {
|
Edit::RemoveEventListener { event_type } => {
|
||||||
if let Some(el) = self.stack.top().dyn_ref::<Element>() {
|
if let Some(el) = self.stack.top().dyn_ref::<Element>() {
|
||||||
el.remove_event_listener_with_callback(
|
// el.remove_event_listener_with_callback(
|
||||||
event_type,
|
// event_type,
|
||||||
self.callback.as_ref().unwrap().as_ref().unchecked_ref(),
|
// self.callback.as_ref().unwrap().as_ref().unchecked_ref(),
|
||||||
)
|
// )
|
||||||
.unwrap();
|
// .unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -62,16 +62,16 @@ impl WebsysRenderer {
|
|||||||
let (sender, mut receiver) = mpsc::unbounded::<EventTrigger>();
|
let (sender, mut receiver) = mpsc::unbounded::<EventTrigger>();
|
||||||
|
|
||||||
let body_element = prepare_websys_dom();
|
let body_element = prepare_websys_dom();
|
||||||
let mut patch_machine = interpreter::PatchMachine::new(body_element.clone());
|
let mut patch_machine = interpreter::PatchMachine::new(body_element.clone(), |_| {});
|
||||||
let root_node = body_element.first_child().unwrap();
|
let root_node = body_element.first_child().unwrap();
|
||||||
patch_machine.stack.push(root_node);
|
patch_machine.stack.push(root_node);
|
||||||
|
|
||||||
// todo: initialize the event registry properly on the root
|
// todo: initialize the event registry properly on the root
|
||||||
|
|
||||||
self.internal_dom
|
self.internal_dom.rebuild()?.iter().for_each(|edit| {
|
||||||
.rebuild()?
|
log::debug!("patching with {:?}", edit);
|
||||||
.iter()
|
patch_machine.handle_edit(edit);
|
||||||
.for_each(|edit| patch_machine.handle_edit(edit));
|
});
|
||||||
|
|
||||||
// Event loop waits for the receiver to finish up
|
// Event loop waits for the receiver to finish up
|
||||||
// TODO! Connect the sender to the virtual dom's suspense system
|
// TODO! Connect the sender to the virtual dom's suspense system
|
||||||
|
|||||||
Reference in new issue
Block a user